Skip to content

Commit 0751781

Browse files
author
karei
committed
modified: README.md
deleted: benchmark/bench_cl.jl modified: benchmark/benchmarks.jl modified: docs/make.jl new file: docs/src/api.md modified: docs/src/index.md modified: src/ComponentLogging.jl modified: src/PlainLogger.jl new file: src/docstrings.jl
1 parent e933d32 commit 0751781

File tree

9 files changed

+490
-160
lines changed

9 files changed

+490
-160
lines changed

README.md

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,175 @@
66
[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor's%20Guide-blueviolet)](https://github.com/SciML/ColPrac)
77
[![PkgEval](https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/C/ComponentLogging.svg)](https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/C/ComponentLogging.html)
88

9-
See the [Changelog](./CHANGELOG.md).
9+
ComponentLogging provides hierarchical control over log levels and messages. It is designed to replace ad‑hoc `print/println` calls and verbose flags inside functions, and to strengthen control flow in Julia programs.
10+
11+
[Changelog](./CHANGELOG.md).
12+
13+
---
14+
15+
## Introduction
16+
17+
Hierarchical logging is critical for building reliable software, especially in compute‑intensive systems. Many computational functions need to emit messages at different detail levels, and there can be lots of such functions. This calls for fine‑grained, per‑function control over logging.
18+
19+
Another challenge is performance: printing intermediate values can be expensive and slow down hot paths. Ideally, those intermediates should be computed and printed only when logging is actually enabled. This requires the ability to alter control flow based on logging decisions.
20+
21+
Julia’s `CoreLogging` module provides a solid foundation, and this package builds on it. At its core is the `ComponentLogger`, which uses a `Dict` keyed by `NTuple{N,Symbol}` to control output hierarchically across a module. You can choose the `LogLevel` per feature, type, module, or function name to achieve global control with precision.
22+
23+
`ComponentLogger` only routes and filters messages. It does not own IO streams. You provide an `AbstractLogger` sink such as `ConsoleLogger` or the included `PlainLogger`. The sink determines where and how messages are written.
24+
25+
### Features
26+
- High performance; negligible overhead when logging is disabled.
27+
- Suited for controlling module‑wide output granularity using one (or a few) loggers.
28+
- Enables control‑flow changes based on hierarchical log levels to eliminate unnecessary computations from hot paths.
29+
30+
---
31+
32+
## Installation
33+
34+
```julia
35+
julia>] add ComponentLogging
36+
```
37+
38+
---
39+
40+
## Quick Start
41+
42+
The following is a general pattern you can copy and adapt.
43+
44+
```julia
45+
using ComponentLogging
46+
47+
sink = PlainLogger()
48+
rules = Dict(
49+
:core => Info,
50+
:io => Warn,
51+
:net => Debug
52+
)
53+
clogger = ComponentLogger(rules; sink)
54+
```
55+
Output:
56+
```julia
57+
ComponentLogger
58+
sink: PlainLogger
59+
min: Debug
60+
rules: 4
61+
├─ :__default__ Info
62+
├─ :core Info
63+
├─ :io Warn
64+
└─ :net Debug
65+
```
66+
67+
Convenience forwarding helpers (short paths)
68+
69+
```julia
70+
set_log_level(group, level) = ComponentLogging.set_log_level!(clogger, group, level)
71+
with_min_level(f, level) = ComponentLogging.with_min_level(f, clogger, level)
72+
clog(group, level, message...; file=nothing, line=nothing, kwargs...) =
73+
ComponentLogging.clog(clogger, group, level, message...; file, line, kwargs...)
74+
clogenabled(group, level) = ComponentLogging.clogenabled(clogger, group, level)
75+
clogf(f::F, group, level) where {F<:Function} = ComponentLogging.clogf(f, clogger, group, level)
76+
```
77+
78+
### `clog` usage
79+
80+
`clog(group::Union{Symbol,NTuple{N,Symbol}}, level::Union{Integer,LogLevel}, message...)`
81+
82+
```julia
83+
function foo(a)
84+
a > 0 || clog(:core, 1000, "a should be positive")
85+
a += 1
86+
clog(:core, 0, "a is now $a")
87+
return a
88+
end
89+
```
90+
91+
Here `level` can be an `Integer` or a `LogLevel`. When it is an integer, it is interpreted as `LogLevel(Integer)`. The common mapping is 0 => Info, −1000 => Debug, 1000 => Warn, 2000 => Error.
92+
93+
### `clogenabled` usage
94+
95+
`clogenabled(group::Union{Symbol,NTuple{N,Symbol}}, level::Union{Integer,LogLevel})`
96+
97+
`clogenabled` checks whether a given component is enabled at a given level. It is intended to drive control‑flow decisions so that certain code runs only when logging is enabled. Returns `Bool`.
98+
99+
```julia
100+
function compute_sumsq()
101+
arr = randn(1000)
102+
sumsq = 0.0
103+
for i in eachindex(arr)
104+
x = arr[i]
105+
sumsq += x^2
106+
if clogenabled(:core, 1)
107+
# Compute mean and standard deviation (intermediates) only when logging is enabled
108+
meanval = mean(arr[1:i])
109+
stdval = std(arr[1:i])
110+
clog(:core, 1, "i=$i, x=$x, mean=$(meanval), std=$(stdval), sumsq=$(sumsq)")
111+
end
112+
end
113+
end
114+
```
115+
116+
By guarding with `clogenabled`, intermediate computations are performed only when logs will be emitted, maximizing performance.
117+
118+
### `clogf` usage
119+
120+
`clogf(f::Function, group::Union{Symbol,NTuple{N,Symbol}}, level::Union{Integer,LogLevel})`
121+
122+
`clogf` is similar to `clogenabled`: when logging is enabled, it executes the `do`‑block as a zero‑argument function and logs its return value. When disabled, the block is skipped entirely.
123+
124+
```julia
125+
function compute_sumsq()
126+
arr = randn(1000)
127+
sumsq = 0.0
128+
for i in eachindex(arr)
129+
x = arr[i]
130+
sumsq += x^2
131+
clogf(:core, 1) do
132+
meanval = mean(arr[1:i])
133+
stdval = std(arr[1:i])
134+
# The return value will be used as the log message
135+
"i=$i, x=$x, mean=$(meanval), std=$(stdval), sumsq=$(sumsq)"
136+
end
137+
end
138+
end
139+
```
140+
141+
### `with_min_level` usage
142+
143+
`with_min_level(f::Function, logger::ComponentLogger, level::Union{Integer,LogLevel})`
144+
145+
`with_min_level` temporarily sets the logger’s global minimum level and restores it on exit.
146+
147+
For example, to benchmark `compute_sumsq()` without any logging‑related work:
148+
149+
```julia
150+
with_min_level(2000) do
151+
@benchmark compute_sumsq()
152+
end
153+
```
154+
155+
The function API is the primary entry point. Macro helpers are also provided for convenience. See the [Documentation](https://abcdvvvv.github.io/ComponentLogging.jl/dev/).
156+
157+
## `PlainLogger`
158+
159+
`PlainLogger` is roughly a `Base.CoreLogging.SimpleLogger` without the `[Info:`‑style prefixes. Its output looks like `print`/`println`. It writes messages directly to the console, without additional formatting or filtering beyond color.
160+
161+
`PlainLogger` and `ComponentLogger` are independent. You can also `include("src/PlainLogger.jl")` to use `PlainLogger` on its own.
162+
163+
Example:
164+
165+
```julia
166+
using ComponentLogging, Logging
167+
168+
logger = PlainLogger()
169+
with_logger(logger) do
170+
@info "Hello, Julia!"
171+
end
172+
```
173+
Output:
174+
```julia
175+
Hello, Julia!
176+
@ README.md:165
177+
```
178+
179+
## Comparison with Similar Packages
180+

benchmark/bench_cl.jl

Lines changed: 0 additions & 143 deletions
This file was deleted.

0 commit comments

Comments
 (0)