Skip to main content

Logging

Proper logging is essential for effective debugging, monitoring, and auditing. This guide outlines best practices for implementing logging in the CZERTAINLY codebase. Adhering to these practices ensures consistency and helps maintain high code quality.

Logging in CZERTAINLY services

CZERTAINLY is a microservices-based platform, meaning that each service can have its own logging configuration, specific to its requirements. The logging configuration is managed centrally by the Core of the platform. Connectors and other services can implement their own logging configuration, but they should adhere to the guidelines outlined in this document, adjusted to their specific needs and language.

Logging information

CZERTAINLY uses the system, audit, or event logs for all logging activities. Contributors must follow the standard logging structure as outlined in the Logging Documentation.

Logging levels

Choose the correct logging level for your log statements:

LevelPurposeExample
TRACEVery fine-grained logs, usually for tracing program flow.logger.trace("Entering method getCertificateById")
DEBUGInformation useful for debugging during development.logger.debug("Fetching certificate with ID: {}", id)
INFOGeneral operational messages.logger.info("Certificate issued successfully: {}", certId)
WARNPotential issues that may not require immediate attention.logger.warn("Certificate expiry is nearing: {}", certId)
ERRORErrors that impact functionality.logger.error("Failed to issue certificate: {}", exception)

General guidelines for logging

Use logger configuration

Obtain the logger instance for consistent logging:

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

Log at the correct level

Use the appropriate log level for each message. Avoid using a higher log level than necessary.

Bad Example:

logger.error("Certificate issued successfully: {}", certId);

Good Example:

logger.debug("This is a debug message.");
logger.info("Service started successfully.");
logger.warn("Resource usage is nearing the limit.");
logger.error("Failed to process the request due to {}", exception.getMessage());

Avoid sensitive data in logs

Never log sensitive data like passwords, private keys, secrets, or personally identifiable information (PII).

Bad Example:

logger.info("User credentials: {}", userCredentials);

Use contextual logging

Always provide context in log messages to make them actionable and meaningful. Avoid generic or unclear messages.

Bad Example:

logger.warn("Certificate is expiring soon.");

Good Example:

logger.warn("Certificate {} is nearing expiry. Days remaining: {}", certId, daysRemaining);

Avoid excessive logging

Do not over-log, especially in loops or frequently called methods.

Bad Example:

for (int i = 0; i < items.size(); i++) {
logger.debug("Processing item: {}", items.get(i));
}

Good Example:

logger.info("Processing {} items", items.size());

Do not use System.out.println()

Always use the logging framework instead of System.out.println() for logging messages.

Bad Example:

System.out.println("This is a debug message");

Good Example:

logger.debug("This is a debug message");

Include exception information

Pass exceptions to the logger to include stack traces and error details.

Bad Example:

logger.error("Failed to process request: {}", exception.getMessage());

Good Example:

logger.error("Failed to process request", exception);

Structured logging guidelines

Platform supports the following types of structured logs:

Audit logging

Audit log records all activities and operations initiated by user actions. Therefore, audit logs should be enforced on API endpoints. The AuditLogged annotation should be used to log all user-initiated actions, for example:

@AuditLogged(module = Module.CERTIFICATES, resource = Resource.CERTIFICATE, operation = Operation.LIST)
public CertificateResponseDto listCertificates(SearchRequestDto request) throws ValidationException {
return certificateService.listCertificates(SecurityFilter.create(), request);
}

See AuditLogged annotation definition for more details.

Event logging

Event logs are used to record operation-related events. The event logs are not necessarily user-initiated, but are generated by the system. The LoggerWrapper class should be initialized to log events, for example:

private static final LoggerWrapper logger = new LoggerWrapper(UserManagementServiceImpl.class, Module.AUTH, Resource.USER);

The LoggerWrapper class is a wrapper around the Logger class and provides additional methods to log events.

In the example above, the LoggerWrapper is initialized for the UserManagementServiceImpl class, with module AUTH and resource USER parameters. This configuration ensures will log all events related to user management, for example when the user is successfully created:

logger.logEvent(Operation.CREATE, OperationResult.SUCCESS, response.toLogData(), null);

Structured log format

Audit and event logs should follow a structured format. Platform automatically generates the log message based on the provided parameters. The log message should be comply with the JSON schema. See Log Record Structure for more details.

Useful resources for structured logging

Logging Configuration

Logging configuration is centrally managed. Contributors should avoid altering the logging configuration unless necessary. Refer to the Logging Settings for details.

Reviewing Logs

Before submitting a pull request:

  • Verify that all log messages are clear and actionable.
  • Ensure sensitive data is excluded from logs.
  • Test the application to confirm that logs provide the necessary context.