├── force-app
└── main
│ └── default
│ ├── classes
│ ├── Log.cls-meta.xml
│ ├── Log_Test.cls-meta.xml
│ ├── Log_Test.cls
│ └── Log.cls
│ ├── objects
│ └── Log__c
│ │ ├── listViews
│ │ └── All.listView-meta.xml
│ │ ├── fields
│ │ ├── Class__c.field-meta.xml
│ │ ├── Method__c.field-meta.xml
│ │ ├── Message__c.field-meta.xml
│ │ ├── Context__c.field-meta.xml
│ │ └── Line__c.field-meta.xml
│ │ └── Log__c.object-meta.xml
│ ├── tabs
│ └── Log__c.tab-meta.xml
│ └── layouts
│ └── Log__c-Log Layout.layout-meta.xml
├── package.xml
├── LICENSE
└── README.md
/force-app/main/default/classes/Log.cls-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 45.0
4 | Active
5 |
6 |
--------------------------------------------------------------------------------
/force-app/main/default/classes/Log_Test.cls-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 45.0
4 | Active
5 |
6 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/listViews/All.listView-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | All
4 | Everything
5 |
6 |
7 |
--------------------------------------------------------------------------------
/force-app/main/default/tabs/Log__c.tab-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | Technical object, storing application logs.
5 | Custom99: CRT TV
6 |
7 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/fields/Class__c.field-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Class__c
4 | Apex Class Name.
5 | false
6 | Apex Class Name.
7 |
8 | 80
9 | false
10 | false
11 | Text
12 | false
13 |
14 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/fields/Method__c.field-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Method__c
4 | Apex Method Name.
5 | false
6 | Apex Method Name.
7 |
8 | 255
9 | false
10 | false
11 | Text
12 | false
13 |
14 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/fields/Message__c.field-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Message__c
4 | Error Message or Description
5 | false
6 | Error Message or Description
7 |
8 | 32768
9 | false
10 | LongTextArea
11 | 5
12 |
13 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/fields/Context__c.field-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Context__c
4 | Context (ID or Timestamp).
5 | false
6 | Context (ID or Timestamp).
7 |
8 | 255
9 | false
10 | false
11 | Text
12 | false
13 |
14 |
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/fields/Line__c.field-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Line__c
4 | Line number in the Apex Class.
5 | false
6 | Line number in the Apex Class.
7 |
8 | 18
9 | false
10 | 0
11 | false
12 | Number
13 | false
14 |
15 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Log
5 | Log_Test
6 | ApexClass
7 |
8 |
9 | Log__c
10 | CustomObject
11 |
12 |
13 | Log__c
14 | CustomTab
15 |
16 |
17 | Log__c-Log Layout
18 | Layout
19 |
20 |
21 | Log__c.All
22 | ListView
23 |
24 | 45.0
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Mehdi Cherfaoui
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Oct'2023 update: I don't recommend using this framework anymore. I don't maintain it.**
2 |
3 | **A better alternative is to use Nebula Logger for Salesforce: https://github.com/jongpie/NebulaLogger**
4 |
5 |
6 | # sfdx-ErrorLoggingFramework
7 |
8 |
9 | A basic error logging framework for Apex, derived from https://github.com/rsoesemann/apex-unified-logging
10 |
11 |
12 | The purpose of this version is to store the logs into a custom object (so that it can be persisted longer than a platform event).
13 |
14 |
15 | To add this error logging framework to your sfdx project, you can clone this repo and then push it to your scratch org by executing the command `sfdx force:source:deploy -p `, followed by `sfdx force:source:retrieve -x `
16 | to retrieve the metadata in your sfdx project folder.
17 |
18 |
19 | ## Example Use:
20 |
21 | ### To log all errors of a bulk DML operation:
22 | ```apex
23 | // Create two accounts, one of which is missing a required field
24 | Account[] accountList = new List{
25 | new Account(Name='Account1'),
26 | new Account()};
27 | Database.SaveResult[] srList = Database.insert(accountList, false);
28 |
29 | // Log errors
30 | Log.error(srList);
31 | ```
32 |
33 | ### To log a single error:
34 | ```apex
35 | try{
36 | //try something complex
37 | }catch (exception pokemon){
38 | // Log error
39 | Log.error(pokemon.getMessage())
40 | }
41 | ```
42 |
43 | ### To log a single error related to a record or job Id:
44 | ```apex
45 | try{
46 | Update myRecord;
47 | }catch (exception pokemon){
48 | // Log error
49 | Log.error(pokemon.getMessage(), myRecord.Id)
50 | }
51 | ```
52 | ~~
53 |
--------------------------------------------------------------------------------
/force-app/main/default/classes/Log_Test.cls:
--------------------------------------------------------------------------------
1 | @IsTest
2 | private class Log_Test {
3 |
4 | @IsTest
5 | private static void singleErrorTest() {
6 | Test.startTest();
7 | Account[] acctsFail = new List{new Account()};
8 | Database.SaveResult[] srList = Database.insert(acctsFail, false);
9 | Log.error('test error'); //1 log record
10 | Log.error('test error Id', acctsFail[0].Id); //1 additional log record
11 | Log.error('test error List', srList); //1 log record
12 | Log.error('test error List and Id', srList, acctsFail[0].Id); //1 additional log record
13 | Test.stopTest();
14 | //Verify
15 | List errorLogs = [SELECT Id FROM Log__c];
16 | System.assertEquals(4, errorLogs.size(), 'No Log created');
17 | }
18 |
19 | @IsTest
20 | private static void bulkInsertErrorTest() {
21 | Test.startTest();
22 | Account[] acctsFail = new List{new Account()};
23 | Database.SaveResult[] srList = Database.insert(acctsFail, false);
24 | Log.error(srList); //1 log record
25 | Test.stopTest();
26 | //Verify
27 | List errorLogs = [SELECT Id FROM Log__c];
28 | System.assertEquals(1, errorLogs.size(), 'No Log created');
29 | }
30 |
31 | @IsTest
32 | private static void bulkUpsertErrorTest() {
33 | Test.startTest();
34 | Account[] acctsFail = new List{new Account()};
35 | Database.UpsertResult[] urList = Database.upsert(acctsFail, false);
36 | Log.error(urList); //1 log record
37 | Test.stopTest();
38 | //Verify
39 | List errorLogs = [SELECT Id FROM Log__c];
40 | System.assertEquals(1, errorLogs.size(), 'No Log created');
41 | }
42 |
43 | @IsTest
44 | private static void bulkDeleteErrorTest() {
45 | Test.startTest();
46 | Pricebook2 standardPricebook = new Pricebook2(Id = Test.getStandardPricebookId());
47 | Database.DeleteResult[] drList = Database.delete(new List{standardPricebook}, false);
48 | Log.error(drList); //1 log record
49 | Test.stopTest();
50 | //Verify
51 | List errorLogs = [SELECT Id FROM Log__c];
52 | System.assertEquals(1, errorLogs.size(), 'No Log created');
53 | }
54 |
55 |
56 |
57 | }
--------------------------------------------------------------------------------
/force-app/main/default/objects/Log__c/Log__c.object-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Accept
5 | Default
6 |
7 |
8 | CancelEdit
9 | Default
10 |
11 |
12 | Clone
13 | Default
14 |
15 |
16 | Delete
17 | Default
18 |
19 |
20 | Edit
21 | Default
22 |
23 |
24 | List
25 | Default
26 |
27 |
28 | New
29 | Default
30 |
31 |
32 | SaveEdit
33 | Default
34 |
35 |
36 | Tab
37 | Default
38 |
39 |
40 | View
41 | Default
42 |
43 | false
44 | SYSTEM
45 | Deployed
46 | Technical object, storing application logs and errors.
47 | false
48 | true
49 | false
50 | false
51 | false
52 | true
53 | true
54 | true
55 | true
56 | Private
57 |
58 |
59 | Log-{0000}
60 |
61 | AutoNumber
62 |
63 | Logs
64 |
65 | Read
66 | Public
67 |
68 |
--------------------------------------------------------------------------------
/force-app/main/default/layouts/Log__c-Log Layout.layout-meta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ChangeOwnerOne
4 | ChangeRecordType
5 | Clone
6 | Delete
7 | Edit
8 | PrintableView
9 | Share
10 | Submit
11 |
12 | false
13 | false
14 | true
15 |
16 |
17 |
18 | Readonly
19 | CreatedById
20 |
21 |
22 | Edit
23 | Context__c
24 |
25 |
26 |
27 |
28 | Edit
29 | Class__c
30 |
31 |
32 | Edit
33 | Method__c
34 |
35 |
36 | Edit
37 | Line__c
38 |
39 |
40 |
41 |
42 |
43 | true
44 | true
45 | false
46 |
47 |
48 |
49 | Edit
50 | Message__c
51 |
52 |
53 |
54 |
55 |
56 | false
57 | false
58 | true
59 |
60 |
61 |
62 | Readonly
63 | Name
64 |
65 |
66 |
67 |
68 |
69 |
70 | true
71 | false
72 | true
73 |
74 |
75 |
76 |
77 |
78 |
79 | false
80 | false
81 | false
82 | false
83 | false
84 |
85 | 00h5E000000YXWp
86 | 4
87 | 0
88 | Default
89 |
90 |
91 |
--------------------------------------------------------------------------------
/force-app/main/default/classes/Log.cls:
--------------------------------------------------------------------------------
1 | /**
2 | Logging utility. Derived from https://github.com/rsoesemann/apex-unified-logging
3 | **/
4 | public with sharing class Log {
5 |
6 | private static final String ORG_NAMESPACE = [SELECT NamespacePrefix FROM Organization LIMIT 1].NamespacePrefix;
7 | private static final String CLASSNAME = Log.class.getName();
8 | private static final Pattern STACK_LINE = Pattern.compile('^(?:Class\\.)?([^.]+)\\.?([^\\.\\:]+)?[\\.\\:]?([^\\.\\:]*): line (\\d+), column (\\d+)$');
9 | private static final String DEFAULT_CONTEXT = timestamp();
10 |
11 |
12 | /**
13 | * @description: Logs a simple error. Result in a Log__c record being inserted, so do not call it in a Loop!
14 | * @params: message (short description of the error)
15 | * @return: void
16 | */
17 | public static void error(String message) {
18 | error(message, new List