Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bf894d7
[Automated] Update the native jar versions
randilt Jan 14, 2025
e656072
Update Ballerina.toml version and add raw template logging functionality
randilt Jan 14, 2025
3c924f1
Add test for raw template check (ERROR LEVEL)
randilt Jan 14, 2025
cb4e339
Enhance logging messages in raw template with prefixes and add tests …
randilt Jan 14, 2025
146da42
Revert autoformat
randilt Jan 14, 2025
80085d6
Fix regex syntax in logfmt test cases for consistent line splitting
randilt Jan 14, 2025
678ae26
Update Ballerina.toml and logfmt tests to use dynamic versioning and …
randilt Jan 14, 2025
b40b92d
Fix indentation in logfmt test cases for improved readability
randilt Jan 14, 2025
436adcf
Fix formatting in exec function signature for consistency
randilt Jan 14, 2025
d54ed1e
Apply suggestions from code review
randilt Jan 16, 2025
113f52c
Add comment for raw templates in logging for clarity
randilt Jan 16, 2025
6cc6993
Merge branch 'raw-template-logging-implementation' of https://github.…
randilt Jan 16, 2025
6878232
Refactor logging functions to directly print messages and enhance log…
randilt Jan 16, 2025
f2c2b1d
Simplify value assignment in print function using ternary operators f…
randilt Jan 16, 2025
7abe611
Refactor print function to always process messages, simplifying messa…
randilt Jan 16, 2025
cf176c1
Refactor processMessage function to use a concise arrow function synt…
randilt Jan 17, 2025
5378507
Change insertions type in PrintableRawTemplate from anydata[] to Valu…
randilt Jan 17, 2025
e4147fd
Add support for logging raw template values in log APIs and update re…
randilt Jan 17, 2025
3ec3b58
Update printDebug function documentation
randilt Jan 17, 2025
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
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.11.0-20241121-075100-c4c87cbc"
distribution-version = "2201.11.0-20241218-101200-109f6cc7"

[[package]]
org = "ballerina"
Expand Down
74 changes: 62 additions & 12 deletions ballerina/natives.bal
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
// under the License.

import ballerina/io;
import ballerina/observe;
import ballerina/jballerina.java;
import ballerina/lang.value;
import ballerina/observe;

# Represents log level types.
enum LogLevel {
Expand All @@ -28,7 +28,16 @@ enum LogLevel {
}

# A value of `anydata` type or a function pointer.
public type Value anydata|Valuer;
public type Value anydata|Valuer|PrintableRawTemplate;

# Represents raw templates for logging.
# e.g: `The input value is ${val}`
# + strings - String values of the template as an array
# + insertions - Parameterized values/expressions after evaluations as an array
public type PrintableRawTemplate object {
public string[] & readonly strings;
public anydata[] insertions;
};

# A function, which returns `anydata` type.
public type Valuer isolated function () returns anydata;
Expand Down Expand Up @@ -82,19 +91,53 @@ public enum FileWriteOption {
APPEND
}

class PrintableRawTemplateImpl {
*object:RawTemplate;
public Value[] insertions;

public isolated function init(PrintableRawTemplate printableRawTemplate) {
self.strings = printableRawTemplate.strings;
self.insertions = printableRawTemplate.insertions;
}

public isolated function toString() returns string {
Value[] templateInsertions = self.insertions;
string[] templateStrings = self.strings;
string templatedString = templateStrings[0];
foreach int i in 1 ..< (templateStrings.length()) {
Value templateInsert = templateInsertions[i - 1];
if templateInsert is PrintableRawTemplate {
templatedString += new PrintableRawTemplateImpl(templateInsert).toString() + templateStrings[i];
} else if templateInsert is Valuer {
templatedString += templateInsert().toString() + templateStrings[i];
} else {
templatedString += templateInsert.toString() + templateStrings[i];
}
}
return templatedString;
}
}

isolated function processMessage(string|PrintableRawTemplate msg) returns string {
if msg is PrintableRawTemplate {
return new PrintableRawTemplateImpl(msg).toString();
}
return msg;
}

# Prints debug logs.
# ```ballerina
# log:printDebug("debug message", id = 845315)
# log:printDebug(`Debug message with value: ${value}`, id = 845315)
# ```
#
# + msg - The message to be logged
# + 'error - The error struct to be logged
# + stackTrace - The error stack trace to be logged
# + keyValues - The key-value pairs to be logged
public isolated function printDebug(string msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
public isolated function printDebug(string|PrintableRawTemplate msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
// Added `stackTrace` as an optional param due to https://github.com/ballerina-platform/ballerina-lang/issues/34572
if isLogLevelEnabled(DEBUG, getModuleName(keyValues)) {
print(DEBUG, msg, 'error, stackTrace, keyValues);
print(DEBUG, processMessage(msg), 'error, stackTrace, keyValues);
}
}

Expand All @@ -108,9 +151,9 @@ public isolated function printDebug(string msg, error? 'error = (), error:StackF
# + 'error - The error struct to be logged
# + stackTrace - The error stack trace to be logged
# + keyValues - The key-value pairs to be logged
public isolated function printError(string msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
public isolated function printError(string|PrintableRawTemplate msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
if isLogLevelEnabled(ERROR, getModuleName(keyValues)) {
print(ERROR, msg, 'error, stackTrace, keyValues);
print(ERROR, processMessage(msg), 'error, stackTrace, keyValues);
}
}

Expand All @@ -123,9 +166,9 @@ public isolated function printError(string msg, error? 'error = (), error:StackF
# + 'error - The error struct to be logged
# + stackTrace - The error stack trace to be logged
# + keyValues - The key-value pairs to be logged
public isolated function printInfo(string msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
public isolated function printInfo(string|PrintableRawTemplate msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
if isLogLevelEnabled(INFO, getModuleName(keyValues)) {
print(INFO, msg, 'error, stackTrace, keyValues);
print(INFO, processMessage(msg), 'error, stackTrace, keyValues);
}
}

Expand All @@ -138,9 +181,9 @@ public isolated function printInfo(string msg, error? 'error = (), error:StackFr
# + 'error - The error struct to be logged
# + stackTrace - The error stack trace to be logged
# + keyValues - The key-value pairs to be logged
public isolated function printWarn(string msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
public isolated function printWarn(string|PrintableRawTemplate msg, error? 'error = (), error:StackFrame[]? stackTrace = (), *KeyValues keyValues) {
if isLogLevelEnabled(WARN, getModuleName(keyValues)) {
print(WARN, msg, 'error, stackTrace, keyValues);
print(WARN, processMessage(msg), 'error, stackTrace, keyValues);
}
}

Expand Down Expand Up @@ -187,7 +230,14 @@ isolated function print(string logLevel, string msg, error? err = (), error:Stac
logRecord["stackTrace"] = stackTraceArray;
}
foreach [string, Value] [k, v] in keyValues.entries() {
anydata value = v is Valuer ? v() : v;
anydata value;
if v is Valuer {
value = v();
} else if v is PrintableRawTemplate {
value = processMessage(v);
} else {
value = v;
}
logRecord[k] = value;
}
if observe:isTracingEnabled() {
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ path = "../test-utils/build/libs/log-test-utils-@project.version@.jar"
[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "io-native"
path = "./lib/io-native-@io.native.version@.jar"
path = "./lib/io-native-@io.native.version@.jar"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
//
// WSO2 Inc. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/log;

public function main() {
string myname = "Alex92";
int myage = 25;
log:printError(string `error: My name is ${myname} and my age is ${myage}`);
log:printWarn(string `warning: My name is ${myname} and my age is ${myage}`);
log:printInfo(string `info: My name is ${myname} and my age is ${myage}`);
log:printDebug(string `debug: My name is ${myname} and my age is ${myage}`);
}

72 changes: 71 additions & 1 deletion integration-tests/tests/tests_logfmt.bal
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
// specific language governing permissions and limitations
// under the License.

import ballerina/jballerina.java;
import ballerina/io;
import ballerina/jballerina.java;
import ballerina/test;

const string UTF_8 = "UTF-8";
Expand All @@ -25,7 +25,9 @@ const string PRINT_INFO_FILE = "tests/resources/samples/print-functions/info.bal
const string PRINT_WARN_FILE = "tests/resources/samples/print-functions/warn.bal";
const string PRINT_DEBUG_FILE = "tests/resources/samples/print-functions/debug.bal";
const string PRINT_ERROR_FILE = "tests/resources/samples/print-functions/error.bal";
const string PRINT_RAW_TEMPLATE_FILE = "tests/resources/samples/print-functions/raw_template.bal";
const string LOG_LEVEL_FILE = "tests/resources/samples/log-levels/main.bal";
const string LOG_LEVEL_RAW_TEMPLATE_FILE = "tests/resources/samples/log-levels-raw-template/main.bal";

const string FILE_WRITE_OUTPUT_OVERWRITE_INPUT_FILE_LOGFMT = "tests/resources/samples/file-write-output/single-file/overwrite-logfmt.bal";
const string FILE_WRITE_OUTPUT_APPEND_INPUT_FILE_LOGFMT = "tests/resources/samples/file-write-output/single-file/append-logfmt.bal";
Expand All @@ -51,6 +53,11 @@ const string MESSAGE_WARN_LOGFMT = " level=WARN module=\"\" message=\"warn log\"
const string MESSAGE_INFO_LOGFMT = " level=INFO module=\"\" message=\"info log\"";
const string MESSAGE_DEBUG_LOGFMT = " level=DEBUG module=\"\" message=\"debug log\"";

const string MESSAGE_ERROR_RAW_TEMPLATE_LOGFMT = " level=ERROR module=\"\" message=\"error: My name is Alex92 and my age is 25\"";
const string MESSAGE_WARN_RAW_TEMPLATE_LOGFMT = " level=WARN module=\"\" message=\"warning: My name is Alex92 and my age is 25\"";
const string MESSAGE_INFO_RAW_TEMPLATE_LOGFMT = " level=INFO module=\"\" message=\"info: My name is Alex92 and my age is 25\"";
const string MESSAGE_DEBUG_RAW_TEMPLATE_LOGFMT = " level=DEBUG module=\"\" message=\"debug: My name is Alex92 and my age is 25\"";

const string MESSAGE_ERROR_MAIN_LOGFMT = " level=ERROR module=myorg/myproject message=\"error log\\t\\n\\r\\\\\\\"\"";
const string MESSAGE_WARN_MAIN_LOGFMT = " level=WARN module=myorg/myproject message=\"warn log\\t\\n\\r\\\\\\\"\"";
const string MESSAGE_INFO_MAIN_LOGFMT = " level=INFO module=myorg/myproject message=\"info log\\t\\n\\r\\\\\\\"\"";
Expand Down Expand Up @@ -215,6 +222,68 @@ public function testDebugLevelLogfmt() returns error? {
validateLog(logLines[8], MESSAGE_DEBUG_LOGFMT);
}

@test:Config {}
public function testErrorLevelRawTemplateLogfmt() returns error? {
Process|error execResult = exec(bal_exec_path, {BAL_CONFIG_FILES: CONFIG_ERROR_LOGFMT}, (), "run", LOG_LEVEL_RAW_TEMPLATE_FILE);
Process result = check execResult;
int _ = check result.waitForExit();
int _ = check result.exitCode();
io:ReadableByteChannel readableResult = result.stderr();
io:ReadableCharacterChannel sc = new (readableResult, UTF_8);
string outText = check sc.read(100000);
string[] logLines = re`\n`.split(outText.trim());
test:assertEquals(logLines.length(), 6, INCORRECT_NUMBER_OF_LINES);
validateLog(logLines[5], MESSAGE_ERROR_RAW_TEMPLATE_LOGFMT);
}

@test:Config {}
public function testWarnLevelRawTemplateLogfmt() returns error? {
Process|error execResult = exec(bal_exec_path, {BAL_CONFIG_FILES: CONFIG_WARN_LOGFMT}, (), "run", LOG_LEVEL_RAW_TEMPLATE_FILE);
Process result = check execResult;
int _ = check result.waitForExit();
int _ = check result.exitCode();
io:ReadableByteChannel readableResult = result.stderr();
io:ReadableCharacterChannel sc = new (readableResult, UTF_8);
string outText = check sc.read(100000);
string[] logLines = re`\n`.split(outText.trim());
test:assertEquals(logLines.length(), 7, INCORRECT_NUMBER_OF_LINES);
validateLog(logLines[5], MESSAGE_ERROR_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[6], MESSAGE_WARN_RAW_TEMPLATE_LOGFMT);
}

@test:Config {}
public function testInfoLevelRawTemplateLogfmt() returns error? {
Process|error execResult = exec(bal_exec_path, {BAL_CONFIG_FILES: CONFIG_INFO_LOGFMT}, (), "run", LOG_LEVEL_RAW_TEMPLATE_FILE);
Process result = check execResult;
int _ = check result.waitForExit();
int _ = check result.exitCode();
io:ReadableByteChannel readableResult = result.stderr();
io:ReadableCharacterChannel sc = new (readableResult, UTF_8);
string outText = check sc.read(100000);
string[] logLines = re`\n`.split(outText.trim());
test:assertEquals(logLines.length(), 8, INCORRECT_NUMBER_OF_LINES);
validateLog(logLines[5], MESSAGE_ERROR_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[6], MESSAGE_WARN_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[7], MESSAGE_INFO_RAW_TEMPLATE_LOGFMT);
}

@test:Config {}
public function testDebugLevelRawTemplateLogfmt() returns error? {
Process|error execResult = exec(bal_exec_path, {BAL_CONFIG_FILES: CONFIG_DEBUG_LOGFMT}, (), "run", LOG_LEVEL_RAW_TEMPLATE_FILE);
Process result = check execResult;
int _ = check result.waitForExit();
int _ = check result.exitCode();
io:ReadableByteChannel readableResult = result.stderr();
io:ReadableCharacterChannel sc = new (readableResult, UTF_8);
string outText = check sc.read(100000);
string[] logLines = re`\n`.split(outText.trim());
test:assertEquals(logLines.length(), 9, INCORRECT_NUMBER_OF_LINES);
validateLog(logLines[5], MESSAGE_ERROR_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[6], MESSAGE_WARN_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[7], MESSAGE_INFO_RAW_TEMPLATE_LOGFMT);
validateLog(logLines[8], MESSAGE_DEBUG_RAW_TEMPLATE_LOGFMT);
}

@test:Config {}
public function testProjectWithoutLogLevelLogfmt() returns error? {
Process|error execResult = exec(bal_exec_path, {}, (), "run", temp_dir_path
Expand Down Expand Up @@ -475,3 +544,4 @@ function exec(@untainted string command, @untainted map<string> env = {},
name: "exec",
'class: "io.ballerina.stdlib.log.testutils.nativeimpl.Exec"
} external;

Loading