diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e22ce98..984a814c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ to include examples, links to docs, or any other relevant information. ### Added +- Added `LoggingConfig.format` to select compact, pretty, or newline-delimited JSON output for + Core logs written to the console. + - Added the `Runtime(disable_environment_info=...)` option to control whether runtime, hosting, and platform information is included in worker heartbeats. diff --git a/temporalio/bridge/Cargo.lock b/temporalio/bridge/Cargo.lock index 97a247eb4..06736d7f0 100644 --- a/temporalio/bridge/Cargo.lock +++ b/temporalio/bridge/Cargo.lock @@ -2825,6 +2825,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" @@ -2836,10 +2846,13 @@ dependencies = [ "once_cell", "parking_lot", "regex-automata", + "serde", + "serde_json", "sharded-slab", "thread_local", "tracing", "tracing-core", + "tracing-serde", ] [[package]] diff --git a/temporalio/bridge/runtime.py b/temporalio/bridge/runtime.py index e5987cb42..fb5b65b4e 100644 --- a/temporalio/bridge/runtime.py +++ b/temporalio/bridge/runtime.py @@ -47,6 +47,7 @@ class LoggingConfig: filter: str forward_to: Callable[[Sequence[BufferedLogEntry]], None] | None + format: str | None @dataclass(frozen=True) diff --git a/temporalio/bridge/sdk-core b/temporalio/bridge/sdk-core index 999e5a7dc..782c738ec 160000 --- a/temporalio/bridge/sdk-core +++ b/temporalio/bridge/sdk-core @@ -1 +1 @@ -Subproject commit 999e5a7dc8bbb8c457322ccb8e1806a0e780be95 +Subproject commit 782c738ecad0a203a2d7070c1c6d0fd053b8d663 diff --git a/temporalio/bridge/src/runtime.rs b/temporalio/bridge/src/runtime.rs index 8a8e7b591..c8fca51e5 100644 --- a/temporalio/bridge/src/runtime.rs +++ b/temporalio/bridge/src/runtime.rs @@ -13,8 +13,8 @@ use temporalio_common::telemetry::metrics::core::MetricCallBufferer; use temporalio_common::telemetry::metrics::CoreMeter; use temporalio_common::telemetry::{ build_otlp_metric_exporter, start_prometheus_metric_exporter, CoreLog, CoreLogStreamConsumer, - Logger, MetricTemporality, OtelCollectorOptions, OtlpProtocol, PrometheusExporterOptions, - TelemetryOptions, + Logger, LoggerFormat, MetricTemporality, OtelCollectorOptions, OtlpProtocol, + PrometheusExporterOptions, TelemetryOptions, }; use temporalio_sdk_core::telemetry::MetricsCallBuffer; use temporalio_sdk_core::{CoreRuntime, TokioRuntimeBuilder}; @@ -48,6 +48,7 @@ pub struct TelemetryConfig { pub struct LoggingConfig { filter: String, forward_to: Option>, + format: Option, } #[pyclass] @@ -121,6 +122,15 @@ pub fn init_runtime(options: RuntimeOptions) -> PyResult { } else { Logger::Console { filter: logging_conf.filter.to_string(), + format: logging_conf + .format + .map(|format| match format.as_str() { + "compact" => Ok(LoggerFormat::Compact), + "pretty" => Ok(LoggerFormat::Pretty), + "json" => Ok(LoggerFormat::Json), + _ => Err(PyValueError::new_err("Unrecognized logging format")), + }) + .transpose()?, } }) } else { diff --git a/temporalio/runtime.py b/temporalio/runtime.py index fc526ca2b..977310e9a 100644 --- a/temporalio/runtime.py +++ b/temporalio/runtime.py @@ -195,6 +195,14 @@ def formatted(self) -> str: return ",".join(parts) +class LoggingFormat(Enum): + """Format for Core logs written to the console.""" + + COMPACT = "compact" + PRETTY = "pretty" + JSON = "json" + + @dataclass(frozen=True) class LoggingConfig: """Configuration for runtime logging.""" @@ -207,6 +215,12 @@ class LoggingConfig: See the :py:class:`LogForwardingConfig` docs for more info. """ + format: LoggingFormat | None = None + """Format for Core logs written to the console. This is ignored when + :py:attr:`forwarding` is set. If unset, Core preserves its existing output + selection, including ``TEMPORAL_CORE_PRETTY_LOGS`` support. + """ + default: ClassVar[LoggingConfig] """Default logging configuration of Core WARN level and other ERROR level. @@ -218,6 +232,7 @@ def _to_bridge_config(self) -> temporalio.bridge.runtime.LoggingConfig: if isinstance(self.filter, str) else self.filter.formatted(), forward_to=None if not self.forwarding else self.forwarding._on_logs, + format=None if not self.format else self.format.value, ) diff --git a/tests/test_runtime.py b/tests/test_runtime.py index e003af768..0eca3edb3 100644 --- a/tests/test_runtime.py +++ b/tests/test_runtime.py @@ -16,6 +16,7 @@ from temporalio.runtime import ( LogForwardingConfig, LoggingConfig, + LoggingFormat, OpenTelemetryConfig, PrometheusConfig, Runtime, @@ -143,6 +144,11 @@ async def log_queue_len() -> int: ) +def test_runtime_console_logging_format(): + config = LoggingConfig(filter="INFO", format=LoggingFormat.JSON)._to_bridge_config() + assert config.format == "json" + + @workflow.defn class TaskFailWorkflow: @workflow.run