Skip to content

Logger #306

@pgajek2

Description

@pgajek2

Feature Request: Add Logger Support to SOQL Lib

Is your feature request related to a problem? Please describe.

Add the ability to plug a logger into SOQL Lib.

Solution A

What a developer/client needs to do to use the logger:

  1. Create an Apex class called SOQLLogger.
  2. The class should implement the SOQL.Logger interface.
global class SOQLLogger implements SOQL.Logger {
    public void log(SOQL.Error error) {
        // Use your own logger here
    }
}

Pros

  • SOQL Lib has no dependency, not even a loose one, on other libraries such as Nebula.
  • SOQL.Error contains all the details about the failed query, such as the exception, query string, access level, etc. and the structure is static.
  • Easy to implement: just create a class called SOQLLogger that implements SOQL.Logger.
  • This is a form of adapter and can be used with any kind of logger. The developer can take whatever information they need from SOQL.Error, transform it, and log it using their own logger.
  • We can have more evens in the future e.g. Exception when SOQL in a loop #243 - sending "warn" event when the same query was executed a few times.

Cons

  • The developer has to follow the naming convention: SOQLLogger.
  • There is an extra step for projects that already use both SOQL Lib and Nebula, because the class still needs to be created and maintained.

Solution B

What a developer/client needs to do to use the logger:

Has Nebula?

Nothing. SOQL Lib will use the Nebula logger when it is found.

Doesn't have Nebula?

Create an SOQLLogger class, but based on System.Callable.

global inherited sharing class SOQLLogger implements System.Callable {
    global Object call(String action, Map<String, Object> args) {
        if (action != 'logError') {
            throw new System.IllegalArgumentException('Unsupported action: ' + action);
        }

        // Your logger here
        // expected log:
        /*
          new Map<String, Object>{
            'loggingLevel' => System.LoggingLevel.ERROR.name(),
            'message' => args.get('message') + '\nQuery: ' + args.get('query'),
            'exception' => args.get('exception'),
            'tags' => args.get('tags'),
            'saveLog' => args.get('saveLog')
          }
        */
    }
}

Pros

  • If the Nebula logger is available in the project, no action is needed. SOQL Lib errors will be logged automatically.

Cons

  • If the Nebula logger is available in the project, errors will be logged automatically, with no way to control that behavior.
  • For projects without Nebula, this is much more complicated and less clean, because developers have to use Map<String, Object> in the same format used by Nebula.
  • Not all query details will be available. For example, queryString, accessLevel, etc. will be missing.
  • tags, saveLog, message, and loggingLevel will be hardcoded in SOQL Lib and will have constant values, so developers cannot change them without modifying SOQL Lib.

Technical Details

Solution A

In SOQL Lib:

public interface Logger {
    void log(SOQL.Error error);
}

public class Error {
    public Exception exception;
    String query;
    AccessLevel accessLevel;
    // Other information about the failed query
}

private static Logger logger {
    get {
        if (logger == null) {
            Type type = Type.forName('SOQLLogger');
            logger = type != null ? (Logger) type.newInstance() : new DefaultLogger(); // ??
        }
        return logger;
    }
}

// toList(), toObject(), etc.
try {
    // Execute query
} catch (Exception e) {
    Error error = ...;
    this.logger.log(error);
}

Solution B

In SOQL Lib:

private static System.Callable logger {
    get {
        if (logger == null) {
            // Nebula by default
            Type loggerType = System.Type.forName('Nebula', 'CallableLogger');
            if (loggerType == null) {
                loggerType = System.Type.forName(null, 'CallableLogger');
            }
            if (loggerType == null) {
                loggerType = System.Type.forName('SOQLLogger');
            }

            logger = loggerType == null ? null : (System.Callable) loggerType.newInstance();
        }
        return logger;
    }
}

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request
No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions