Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "log"
version = "2.16.1"
version = "2.17.0"
authors = ["Ballerina"]
keywords = ["level", "format"]
repository = "https://github.com/ballerina-platform/module-ballerina-log"
Expand All @@ -15,18 +15,18 @@ graalvmCompatible = true
[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "log-native"
version = "2.16.1"
path = "../native/build/libs/log-native-2.16.1.jar"
version = "2.17.0"
path = "../native/build/libs/log-native-2.17.0-SNAPSHOT.jar"

[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "log-compiler-plugin"
version = "2.16.1"
path = "../compiler-plugin/build/libs/log-compiler-plugin-2.16.1.jar"
version = "2.17.0"
path = "../compiler-plugin/build/libs/log-compiler-plugin-2.17.0-SNAPSHOT.jar"

[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "log-test-utils"
version = "2.16.1"
path = "../test-utils/build/libs/log-test-utils-2.16.1.jar"
version = "2.17.0"
path = "../test-utils/build/libs/log-test-utils-2.17.0-SNAPSHOT.jar"
scope = "testOnly"
2 changes: 1 addition & 1 deletion ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ id = "log-compiler-plugin"
class = "io.ballerina.stdlib.log.compiler.LogCompilerPlugin"

[[dependency]]
path = "../compiler-plugin/build/libs/log-compiler-plugin-2.16.1.jar"
path = "../compiler-plugin/build/libs/log-compiler-plugin-2.17.0-SNAPSHOT.jar"
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ modules = [
[[package]]
org = "ballerina"
name = "log"
version = "2.16.1"
version = "2.17.0"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
Expand Down
1 change: 1 addition & 0 deletions ballerina/init.bal
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function init() returns error? {
rootLogger = new RootLogger();
check validateDestinations(destinations);
setModule();
initializeLogConfig(level, modules);
}

isolated function validateDestinations(OutputDestination[] destinations) returns Error? {
Expand Down
35 changes: 28 additions & 7 deletions ballerina/natives.bal
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,7 @@ isolated function replaceString(handle receiver, handle target, handle replaceme
} external;

isolated function isLogLevelEnabled(string loggerLogLevel, string logLevel, string moduleName) returns boolean {
string moduleLogLevel = loggerLogLevel;
if modules.length() > 0 {
if modules.hasKey(moduleName) {
moduleLogLevel = modules.get(moduleName).level;
}
}
return logLevelWeight.get(logLevel) >= logLevelWeight.get(moduleLogLevel);
return checkLogLevelEnabled(loggerLogLevel, logLevel, moduleName);
}

isolated function getModuleName(KeyValues keyValues, int offset = 2) returns string {
Expand All @@ -492,3 +486,30 @@ isolated function getCurrentFileSize(string filePath) returns int = @java:Method
isolated function getTimeSinceLastRotation(string filePath, string policy, int maxFileSize, int maxAgeInMillis, int maxBackupFiles) returns int = @java:Method {'class: "io.ballerina.stdlib.log.Utils"} external;

isolated function rotateLog(string filePath, string policy, int maxFileSize, int maxAgeInMillis, int maxBackupFiles) returns error? = @java:Method {'class: "io.ballerina.stdlib.log.Utils"} external;

// ========== Internal native function declarations for runtime log configuration ==========

isolated function initializeLogConfig(Level rootLevel, table<Module> key(name) & readonly modules) = @java:Method {
'class: "io.ballerina.stdlib.log.LogConfigManager",
name: "initializeConfig"
} external;

isolated function registerLoggerWithIdNative(string loggerId, string logLevel) returns error? = @java:Method {
'class: "io.ballerina.stdlib.log.LogConfigManager",
name: "registerLoggerWithId"
} external;

isolated function registerLoggerInternalNative(string logLevel) returns string = @java:Method {
'class: "io.ballerina.stdlib.log.LogConfigManager",
name: "registerLoggerInternal"
} external;

isolated function checkLogLevelEnabled(string loggerLogLevel, string logLevel, string moduleName) returns boolean = @java:Method {
'class: "io.ballerina.stdlib.log.LogConfigManager",
name: "checkLogLevelEnabled"
} external;

isolated function checkCustomLoggerLogLevelEnabled(string loggerId, string logLevel, string moduleName) returns boolean = @java:Method {
'class: "io.ballerina.stdlib.log.LogConfigManager",
name: "checkCustomLoggerLogLevelEnabled"
} external;
44 changes: 40 additions & 4 deletions ballerina/root_logger.bal
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import ballerina/observe;

# Configuration for the Ballerina logger
public type Config record {|
# Optional unique identifier for this logger.
# If provided, the logger will be visible in the ICP dashboard, and its log level can be modified at runtime.
string id?;
# Log format to use. Default is the logger format configured in the module level
LogFormat format = format;
# Log level to use. Default is the logger level configured in the module level
Expand All @@ -37,6 +40,9 @@ type ConfigInternal record {|
readonly & OutputDestination[] destinations = destinations;
readonly & KeyValues keyValues = {...keyValues};
boolean enableSensitiveDataMasking = enableSensitiveDataMasking;
// Logger ID for custom loggers registered with LogConfigManager
// If set, used for runtime log level checking
string loggerId?;
|};

final string ICP_RUNTIME_ID_KEY = "icp.runtimeId";
Expand All @@ -60,12 +66,28 @@ public isolated function fromConfig(*Config config) returns Logger|Error {
foreach [string, Value] [k, v] in config.keyValues.entries() {
newKeyValues[k] = v;
}
Config newConfig = {

// Register with LogConfigManager based on whether user provided an ID
string? loggerId;
if config.id is string {
// User provided ID - register as visible logger (ICP can configure)
error? regResult = registerLoggerWithIdNative(<string>config.id, config.level);
if regResult is error {
return error Error(regResult.message());
}
loggerId = config.id;
} else {
// No user ID - register as internal logger (not visible to ICP)
loggerId = registerLoggerInternalNative(config.level);
}

ConfigInternal newConfig = {
format: config.format,
level: config.level,
destinations: config.destinations,
keyValues: newKeyValues.cloneReadOnly(),
enableSensitiveDataMasking: config.enableSensitiveDataMasking
enableSensitiveDataMasking: config.enableSensitiveDataMasking,
loggerId
};
return new RootLogger(newConfig);
}
Expand All @@ -78,13 +100,21 @@ isolated class RootLogger {
private final readonly & OutputDestination[] destinations;
private final readonly & KeyValues keyValues;
private final boolean enableSensitiveDataMasking;
// Unique ID for custom loggers registered with LogConfigManager
private final string? loggerId;

public isolated function init(Config|ConfigInternal config = <Config>{}) {
self.format = config.format;
self.level = config.level;
self.destinations = config.destinations;
self.keyValues = config.keyValues;
self.enableSensitiveDataMasking = config.enableSensitiveDataMasking;
// Use loggerId from ConfigInternal if present (for custom loggers and derived loggers)
if config is ConfigInternal {
self.loggerId = config.loggerId;
} else {
self.loggerId = ();
}
}

public isolated function printDebug(string|PrintableRawTemplate msg, error? 'error, error:StackFrame[]? stackTrace, *KeyValues keyValues) {
Expand Down Expand Up @@ -117,13 +147,19 @@ isolated class RootLogger {
level: self.level,
destinations: self.destinations,
keyValues: newKeyValues.cloneReadOnly(),
enableSensitiveDataMasking: self.enableSensitiveDataMasking
enableSensitiveDataMasking: self.enableSensitiveDataMasking,
// Preserve the logger ID so derived loggers use the same runtime-configurable level
loggerId: self.loggerId
};
return new RootLogger(config);
}

isolated function print(string logLevel, string moduleName, string|PrintableRawTemplate msg, error? err = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
if !isLogLevelEnabled(self.level, logLevel, moduleName) {
// Check log level - use custom logger check if registered, otherwise use standard check
boolean isEnabled = self.loggerId is string ?
checkCustomLoggerLogLevelEnabled(<string>self.loggerId, logLevel, moduleName) :
isLogLevelEnabled(self.level, logLevel, moduleName);
if !isEnabled {
return;
}
LogRecord logRecord = {
Expand Down
Loading