├── .gitignore ├── .project ├── .settings └── com.salesforce.ide.core.prefs ├── README.md ├── salesforce.schema └── src ├── applications └── ApexLogger.app ├── classes ├── Logger.cls ├── Logger.cls-meta.xml ├── LoggerBackup.cls ├── LoggerBackup.cls-meta.xml ├── LoggerFactory.cls ├── LoggerFactory.cls-meta.xml ├── LoggerRecycleBin.cls ├── LoggerRecycleBin.cls-meta.xml ├── LoggerRecycleBinBatch.cls ├── LoggerRecycleBinBatch.cls-meta.xml ├── LoggerRecycleBinSchedule.cls ├── LoggerRecycleBinSchedule.cls-meta.xml ├── LoggerSettings.cls ├── LoggerSettings.cls-meta.xml ├── logger_TEST.cls └── logger_TEST.cls-meta.xml ├── layouts └── Log__c-Log Layout.layout ├── objects ├── Log__c.object └── LoggerSettings__c.object ├── package.xml ├── remoteSiteSettings └── Loggly.remoteSite └── tabs └── Log__c.tab /.gitignore: -------------------------------------------------------------------------------- 1 | /salesforce.schema 2 | /Referenced Packages 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | apex-logger 4 | 5 | 6 | 7 | 8 | 9 | com.salesforce.ide.builder.online 10 | 11 | 12 | 13 | 14 | com.salesforce.ide.builder.default 15 | 16 | 17 | 18 | 19 | 20 | com.salesforce.ide.nature.default 21 | com.salesforce.ide.nature.online 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/com.salesforce.ide.core.prefs: -------------------------------------------------------------------------------- 1 | #Mon Aug 20 15:13:36 BST 2012 2 | eclipse.preferences.version=1 3 | endpointApiVersion=25.0 4 | endpointEnvironment=Production/Developer Edition 5 | endpointServer=www.salesforce.com 6 | httpsProtocol=true 7 | ideVersion=25.0 8 | keependpoint=false 9 | metadataFormatVersion=25.0 10 | namespacePrefix=logger 11 | packageName=apexlogger 12 | readTimeout=400 13 | username=andyjmahood@gmail.com.log 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | apex-logger 2 | =========== 3 | 4 | Apex Logger replaces system.debug with logging in force.com projects -------------------------------------------------------------------------------- /salesforce.schema: -------------------------------------------------------------------------------- 1 | place holder -------------------------------------------------------------------------------- /src/applications/ApexLogger.app: -------------------------------------------------------------------------------- 1 | 2 | 3 | Log__c 4 | 5 | Log__c 6 | 7 | -------------------------------------------------------------------------------- /src/classes/Logger.cls: -------------------------------------------------------------------------------- 1 | global without sharing class Logger { 2 | 3 | private String logName; 4 | private String logBody; 5 | 6 | global Logger(string logName) { 7 | this.logName = logName + ' - ' + Userinfo.getUserName(); 8 | this.logBody = ''; 9 | 10 | log('********** ' + logName + ' **********'); 11 | log('ORG: ' + Userinfo.getOrganizationName() + '[' + Userinfo.getOrganizationId() + ']'); 12 | log('USER: ' + Userinfo.getUserName() + '[' + Userinfo.getUserId() + ']'); 13 | } 14 | 15 | /* 16 | writes a line into the log with date stamp 17 | */ 18 | global void log(String message) { 19 | system.debug(message); 20 | logBody = logBody + '\n' + system.now().format() + ': ' + message; 21 | } 22 | 23 | /* 24 | persists log to database, additional method will hang off this 25 | eg. send log to loggr/loggly 26 | */ 27 | global void commitLog() { 28 | try { 29 | Log__c log = buildLog(); 30 | 31 | insert log; 32 | logBody=''; 33 | } catch (Exception e) { 34 | system.debug('Failed to commit logs: ' + e); 35 | } 36 | } 37 | 38 | global string getLogName() { 39 | return logName; 40 | } 41 | 42 | /* 43 | creates the Log__c object to be persisted in databasedotcom 44 | */ 45 | private Log__c buildLog() { 46 | Log__c log = new Log__c(); 47 | if (logBody.length() > 32000) { 48 | logBody = logBody.substring(0, 31999); 49 | } 50 | log.Body__c = logBody; 51 | log.Name = logName; 52 | return log; 53 | } 54 | } -------------------------------------------------------------------------------- /src/classes/Logger.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerBackup.cls: -------------------------------------------------------------------------------- 1 | public without sharing class LoggerBackup { 2 | 3 | private class callOutException extends Exception{} 4 | public boolean ENABLED; 5 | public string ENDPOINT; 6 | 7 | public LoggerBackup(){ 8 | ENABLED = LoggerSettings.getBooleanValue('LOGGLY_ENABLED'); 9 | ENDPOINT = LoggerSettings.getValue('LOGGLY_ENDPOINT'); 10 | 11 | system.debug('LoggerBackup...ENABLED...' + ENABLED); 12 | system.debug('LoggerBackup...ENDPOINT...' + ENDPOINT); 13 | } 14 | 15 | public Boolean sendBackUp(String logBody) { 16 | if (!ENABLED) return true; //didnt happen but didnt need it backup anyway 17 | 18 | HttpRequest req = getHttpRequest(logBody); 19 | HttpResponse res = sendLog(req); 20 | return handleResponse(res); 21 | } 22 | private HttpRequest getHttpRequest(String logBody) { 23 | // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint 24 | HttpRequest req = new HttpRequest(); 25 | req.setEndpoint(ENDPOINT); 26 | req.setHeader('Content-Type', 'text/plain'); 27 | req.setMethod('POST'); 28 | req.setBody(logBody); 29 | 30 | return req; 31 | } 32 | private HttpResponse sendLog(HttpRequest req) { 33 | //Instantiate a new http object 34 | Http h = new Http(); 35 | // Send the request, and return a response 36 | HttpResponse res = h.send(req); 37 | 38 | return res; 39 | } 40 | private Boolean handleResponse(HttpResponse res) { 41 | 42 | if (Test.isRunningTest() || res.getStatusCode()==200) { 43 | return true; 44 | } else { 45 | throw new callOutException('Error calling logging service: ' + res); 46 | return false; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/classes/LoggerBackup.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerFactory.cls: -------------------------------------------------------------------------------- 1 | public with sharing class LoggerFactory { 2 | private static Map loggers = new Map(); 3 | 4 | public static Logger getLogger() { 5 | return getLogger('Debug Log'); 6 | } 7 | public static Logger getLogger(String name) { 8 | if (loggers.containsKey(name)) { 9 | return loggers.get(name); 10 | } else { 11 | System.debug('…\n…\n…\nCreating new logger for ' + name); 12 | Logger logger = new Logger(name); 13 | loggers.put(name, logger); 14 | return logger; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/classes/LoggerFactory.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBin.cls: -------------------------------------------------------------------------------- 1 | global with sharing class LoggerRecycleBin { 2 | 3 | global static String CRON_STR = '0 0 * * * ?'; 4 | 5 | global static void purge() { 6 | recycleNow(-1);//will delete all created before tomorrow 7 | } 8 | 9 | global static void recycleNow() { 10 | integer purgeAfterDays = LoggerSettings.getIntegerValue('PURGE_AFTER_DAYS'); 11 | if (purgeAfterDays==null) purgeAfterDays=7;//default to 7 days 12 | recycleNow(purgeAfterDays); 13 | } 14 | 15 | global static void recycleNow(integer dayToRecycle) { 16 | LoggerRecycleBinBatch recycleLogs = new LoggerRecycleBinBatch(); 17 | recycleLogs.query = getLogQuery(dayToRecycle); 18 | 19 | // Invoke the batch job. 20 | ID batchprocessid = Database.executeBatch(recycleLogs, 1); 21 | System.debug('Returned batch process ID: ' + batchProcessId); 22 | } 23 | 24 | global static void schedule() { 25 | 26 | System.schedule('Logger Recycle Bin', CRON_STR, new LoggerRecycleBinSchedule()); 27 | } 28 | 29 | public static string getLogQuery(integer dayToQuery) { 30 | Datetime d = Datetime.now(); 31 | d = d.addDays(-dayToQuery); 32 | 33 | String query = 'SELECT Id, Body__c FROM Log__c WHERE CreatedDate < '+ 34 | d.format('yyyy-MM-dd')+'T'+d.format('HH:mm')+':00.000Z'; 35 | 36 | return query; 37 | } 38 | } -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBin.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBinBatch.cls: -------------------------------------------------------------------------------- 1 | global class LoggerRecycleBinBatch implements Database.Batchable, Database.AllowsCallouts { 2 | public String query; 3 | 4 | global Database.QueryLocator start(Database.BatchableContext BC){ 5 | if (query==null) query = LoggerRecycleBin.getLogQuery(-1); 6 | return Database.getQueryLocator(query); 7 | } 8 | 9 | global void execute(Database.BatchableContext BC, List scope){ 10 | Log__c log = (Log__c)scope[0]; 11 | Boolean safeToDelete=false;//can the delete be processed 12 | 13 | LoggerBackup logBackup = new LoggerBackup(); 14 | safeToDelete = logBackup.sendBackUp(log.Body__c); 15 | 16 | /* 17 | safeToDelete means the backup was successful 18 | or loggly backup disabled 19 | */ 20 | if (safeToDelete) { 21 | delete scope; 22 | DataBase.emptyRecycleBin(scope); 23 | } 24 | } 25 | 26 | global void finish(Database.BatchableContext BC){ 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBinBatch.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBinSchedule.cls: -------------------------------------------------------------------------------- 1 | global class LoggerRecycleBinSchedule implements Schedulable { 2 | 3 | global void execute(SchedulableContext ctx) { 4 | LoggerRecycleBin.recycleNow(); 5 | } 6 | } -------------------------------------------------------------------------------- /src/classes/LoggerRecycleBinSchedule.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/LoggerSettings.cls: -------------------------------------------------------------------------------- 1 | public with sharing class LoggerSettings { 2 | 3 | public static string getValue(string key) { 4 | system.debug('getting value for key...' + key); 5 | 6 | LoggerSettings__c settingValue = LoggerSettings__c.getInstance(key); 7 | if (settingValue!= null && settingValue.Value__c!=null && settingValue.Value__c!='') { 8 | system.debug('returning value for key...' + key + '...value...' + settingValue.Value__c); 9 | return settingValue.Value__c; 10 | } else { 11 | system.debug('returning value for key...' + key + '...value...[null]'); 12 | return null; 13 | } 14 | } 15 | public static integer getIntegerValue(string key) { 16 | string retVal = getValue(key); 17 | return (retVal!=null) ? integer.valueOf(retVal) : null; 18 | } 19 | public static boolean getBooleanValue(string key) { 20 | string retVal = getValue(key); 21 | return (retVal!=null && retVal.equals('TRUE')); 22 | } 23 | } -------------------------------------------------------------------------------- /src/classes/LoggerSettings.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/classes/logger_TEST.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | private class logger_TEST { 3 | 4 | static testMethod void testDefaultLogging() { 5 | Test.startTest(); 6 | 7 | Logger defaultLog = LoggerFactory.getLogger(); 8 | defaultLog.log('This is a default log '); 9 | 10 | //this will concatenate onto the default log 11 | Logger defaultLog2 = LoggerFactory.getLogger(); 12 | defaultLog2.log('This is another on the default default log '); 13 | 14 | //both have same name 15 | system.assertEquals('Debug Log - ' + Userinfo.getUserName(), defaultLog.getLogName()); 16 | system.assertEquals('Debug Log - ' + Userinfo.getUserName(), defaultLog2.getLogName()); 17 | 18 | List logs = null; 19 | logs = [SELECT ID FROM Log__c]; 20 | system.assertEquals(0,logs.size());//no logs committed yet 21 | 22 | defaultLog2.commitLog(); 23 | 24 | logs = [SELECT ID FROM Log__c]; 25 | system.assertEquals(1,logs.size());//one log committed 26 | 27 | LoggerRecycleBin.purge();//purge all logs 28 | 29 | Test.stopTest(); 30 | 31 | } 32 | 33 | static testMethod void testCustomLogging() { 34 | Test.startTest(); 35 | 36 | Logger log = LoggerFactory.getLogger('TESTLOG'); 37 | log.log('This is a log '); 38 | 39 | system.assertEquals('TESTLOG - ' + Userinfo.getUserName(), log.getLogName()); 40 | 41 | List logs = null; 42 | logs = [SELECT ID FROM Log__c]; 43 | system.assertEquals(0,logs.size());//no logs committed yet 44 | 45 | log.commitLog(); 46 | 47 | logs = [SELECT ID FROM Log__c]; 48 | system.assertEquals(1,logs.size());//one log committed 49 | 50 | LoggerRecycleBin.purge();//purge all logs 51 | 52 | Test.stopTest(); 53 | 54 | logs = [SELECT ID FROM Log__c]; 55 | system.assertEquals(0,logs.size());//no logs committed 56 | 57 | 58 | } 59 | 60 | static testmethod void scheudleTest() { 61 | Test.startTest(); 62 | 63 | // Schedule the test job 64 | String jobId = System.schedule('LoggerRecycleBinSchedule', LoggerRecycleBin.CRON_STR, 65 | new LoggerRecycleBinSchedule()); 66 | 67 | CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, 68 | NextFireTime 69 | FROM CronTrigger WHERE id = :jobId]; 70 | 71 | //Verify the expressions are the same 72 | System.assertEquals(LoggerRecycleBin.CRON_STR, ct.CronExpression); 73 | 74 | // Verify the job has not run 75 | System.assertEquals(0, ct.TimesTriggered); 76 | 77 | Test.stopTest(); 78 | 79 | } 80 | 81 | 82 | static testmethod void loggerBackupTest() { 83 | LoggerBackup backup = new LoggerBackup(); 84 | backup.ENABLED = true; 85 | backup.ENDPOINT = '/xxx/'; 86 | 87 | system.assertEquals(true, backup.sendBackUp('to log')); 88 | } 89 | } -------------------------------------------------------------------------------- /src/classes/logger_TEST.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/layouts/Log__c-Log Layout.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | Submit 4 | 5 | false 6 | false 7 | true 8 | 9 | 10 | 11 | Required 12 | Name 13 | 14 | 15 | Readonly 16 | CreatedById 17 | 18 | 19 | 20 | 21 | Edit 22 | OwnerId 23 | 24 | 25 | Readonly 26 | LastModifiedById 27 | 28 | 29 | 30 | 31 | 32 | true 33 | false 34 | false 35 | 36 | 37 | 38 | Edit 39 | Body__c 40 | 41 | 42 | 43 | 44 | 45 | false 46 | false 47 | true 48 | 49 | 50 | 51 | 52 | 53 | 54 | true 55 | false 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 63 | false 64 | true 65 | true 66 | false 67 | false 68 | 69 | 00hd000000M94xw 70 | 4 71 | 0 72 | Default 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/objects/Log__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | Clone 9 | Default 10 | 11 | 12 | Delete 13 | Default 14 | 15 | 16 | Edit 17 | Default 18 | 19 | 20 | List 21 | Default 22 | 23 | 24 | New 25 | Default 26 | 27 | 28 | Tab 29 | Default 30 | 31 | 32 | View 33 | Default 34 | 35 | Deployed 36 | Developer logs of debug events 37 | false 38 | false 39 | false 40 | true 41 | 42 | Body__c 43 | false 44 | 45 | 32000 46 | LongTextArea 47 | 5 48 | 49 | 50 | 51 | All 52 | NAME 53 | CREATEDBY_USER 54 | Everything 55 | 56 | 57 | 58 | 59 | Text 60 | 61 | Logs 62 | 63 | ReadWrite 64 | 65 | -------------------------------------------------------------------------------- /src/objects/LoggerSettings__c.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | List 4 | Public 5 | false 6 | 7 | Value__c 8 | false 9 | 10 | 255 11 | true 12 | Text 13 | false 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | apexlogger 4 | Unrestricted 5 | logger 6 | 25.0 7 | 8 | -------------------------------------------------------------------------------- /src/remoteSiteSettings/Loggly.remoteSite: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | true 5 | https://logs.loggly.com 6 | 7 | -------------------------------------------------------------------------------- /src/tabs/Log__c.tab: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | Custom57: Building Block 6 | 7 | --------------------------------------------------------------------------------