Sitelet https://github.com/ethereum-optimism/optimism/commit/bb59e7e651bece23fae28512f3efd1a934c6041a
Skip to content

Commit bb59e7e

Browse files
authored
kona-sp1-proposer: prefix environment variables (#22411)
Co-authored-by: wwared <541936+wwared@users.noreply.github.com>
1 parent ef695a3 commit bb59e7e

12 files changed

Lines changed: 348 additions & 226 deletions

File tree

‎op-devstack/sysgo/zk_proposer.go‎

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ import (
3030
// emits once config validation and provider construction have completed; the
3131
// launcher matches it to detect startup.
3232
const (
33-
zkProposerReadyMessage = "kona-sp1-proposer started"
34-
konaSP1ELFDirEnv = "KONA_SP1_ELF_DIR"
33+
zkProposerReadyMessage = "kona-sp1-proposer started"
34+
konaSP1ELFDirEnv = "KONA_SP1_ELF_DIR"
35+
konaSP1ProposerEnvPrefix = "KONA_SP1_PROPOSER"
3536
)
3637

38+
func zkProposerEnv(suffix, value string) string {
39+
return konaSP1ProposerEnvPrefix + "_" + suffix + "=" + value
40+
}
41+
3742
func loadZKProgramVKey(elfDir string) (common.Hash, error) {
3843
if elfDir == "" {
3944
return crypto.Keccak256Hash([]byte("kona-sp1-stub-super-aggregation-vkey")), nil
@@ -80,7 +85,7 @@ func WithZKProposalInterval(interval time.Duration) ZKProposerOption {
8085
}
8186

8287
// WithZKFastFinality makes the proposer prove every game it owns while it
83-
// is still unchallenged (FAST_FINALITY_MODE=true).
88+
// is still unchallenged.
8489
func WithZKFastFinality() ZKProposerOption {
8590
return func(cfg *zkProposerConfig) {
8691
cfg.FastFinality = true
@@ -153,7 +158,7 @@ func startZKProposer(
153158
require.NotEmpty(execPath, "kona-sp1-proposer binary path resolved")
154159

155160
// The proposer checks and loads program artifacts at
156-
// PRESTATES_URL/<vkey>.agg.bin.gz and .range.bin.gz, mirroring
161+
// KONA_SP1_PROPOSER_PRESTATES_URL/<vkey>.agg.bin.gz and .range.bin.gz, mirroring
157162
// op-challenger's --prestates-url convention. The aggregation vkey embeds
158163
// the range program's vkey, so it keys both ELFs. Devstack publishes the
159164
// real ELFs, gzipped, when KONA_SP1_ELF_DIR is set; otherwise stub bytes
@@ -195,40 +200,39 @@ func startZKProposer(
195200
require.NoError(os.WriteFile(depSetPath, depSetData, 0o640), "must write interop dependency set")
196201

197202
env := []string{
198-
"L1_RPC=" + l1EL.UserRPC(),
199-
"SUPERROOT_RPC=" + superRootRPC,
200-
"FACTORY_ADDRESS=" + factoryAddr.Hex(),
201-
"PRESTATES_URL=file://" + prestatesDir,
202-
"PRIVATE_KEY=" + hexutil.Encode(crypto.FromECDSA(proposerSecret)),
203+
zkProposerEnv("L1_RPC", l1EL.UserRPC()),
204+
zkProposerEnv("SUPERROOT_RPC", superRootRPC),
205+
zkProposerEnv("FACTORY_ADDRESS", factoryAddr.Hex()),
206+
zkProposerEnv("PRESTATES_URL", "file://"+prestatesDir),
207+
zkProposerEnv("PRIVATE_KEY", hexutil.Encode(crypto.FromECDSA(proposerSecret))),
203208
// The mock provider skips SP1 proving but still runs the full witness
204209
// pipeline against these endpoints and configs.
205-
"PROOF_PROVIDER=mock",
206-
"L1_BEACON_RPC=" + l1CL.beaconHTTPAddr,
210+
zkProposerEnv("PROOF_PROVIDER", "mock"),
211+
zkProposerEnv("L1_BEACON_RPC", l1CL.beaconHTTPAddr),
207212
// Order-irrelevant: kona-host keys L2 providers by queried eth_chainId.
208-
"L2_RPCS=" + strings.Join(l2RPCs, ","),
209-
"ROLLUP_CONFIG_PATHS=" + strings.Join(rollupPaths, ","),
210-
"L1_CONFIG_PATH=" + l1CfgPath,
211-
"DEPENDENCY_SET_PATH=" + depSetPath,
212-
"PROPOSAL_SAFETY=safe",
213-
"FETCH_INTERVAL=2",
214-
"LOG_FORMAT=json",
213+
zkProposerEnv("L2_RPCS", strings.Join(l2RPCs, ",")),
214+
zkProposerEnv("ROLLUP_CONFIG_PATHS", strings.Join(rollupPaths, ",")),
215+
zkProposerEnv("L1_CONFIG_PATH", l1CfgPath),
216+
zkProposerEnv("DEPENDENCY_SET_PATH", depSetPath),
217+
zkProposerEnv("PROPOSAL_SAFETY", "safe"),
218+
zkProposerEnv("FETCH_INTERVAL", "2"),
219+
zkProposerEnv("LOG_FORMAT", "json"),
215220
}
216221
if cfg.ProposalInterval != nil {
217-
env = append(env, "PROPOSAL_INTERVAL_SECONDS="+strconv.FormatUint(uint64(*cfg.ProposalInterval/time.Second), 10))
222+
env = append(env, zkProposerEnv("PROPOSAL_INTERVAL_SECONDS", strconv.FormatUint(uint64(*cfg.ProposalInterval/time.Second), 10)))
218223
}
219224
if cfg.FastFinality {
220-
env = append(env, "FAST_FINALITY_MODE=true")
225+
env = append(env, zkProposerEnv("FAST_FINALITY_MODE", "true"))
221226
}
222-
// Always pin METRICS_PORT: the child inherits the host environment, and
223-
// an inherited value (op-succinct deployments export this exact name)
224-
// would otherwise reach every devstack proposer and collide across
225-
// parallel systems. 0 keeps metrics disabled; "auto" has the proposer
227+
// Always pin the metrics port so inherited host configuration cannot
228+
// reach every devstack proposer and collide across parallel systems.
229+
// 0 keeps metrics disabled; "auto" has the proposer
226230
// bind a free port and report it on its startup line.
227231
metricsPort := "0"
228232
if cfg.Metrics {
229233
metricsPort = "auto"
230234
}
231-
env = append(env, "METRICS_PORT="+metricsPort)
235+
env = append(env, zkProposerEnv("METRICS_PORT", metricsPort))
232236

233237
logOut := logpipe.ToLoggerWithMinLevel(t.Logger().New("component", "kona-sp1-proposer", "src", "stdout"), log.LevelWarn)
234238
logErr := logpipe.ToLoggerWithMinLevel(t.Logger().New("component", "kona-sp1-proposer", "src", "stderr"), log.LevelWarn)

‎rust/kona/sp1/README.md‎

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -172,18 +172,18 @@ rotation, old-signer games stay eligible for defense, resolution, and claims,
172172
but the restart scan does not fast-finalize them.
173173

174174
**Operational requirement**: rotated-out prestate artifacts must remain published
175-
under `PRESTATES_URL` for as long as games created under them can be live, or the
175+
under `KONA_SP1_PROPOSER_PRESTATES_URL` for as long as games created under them can be live, or the
176176
proposer loses the ability to defend, resolve, and claim those games.
177177

178178
### Proof providers
179179

180-
- `PROOF_PROVIDER=network`: real SP1 proving via the Succinct Prover Network.
180+
- `KONA_SP1_PROPOSER_PROOF_PROVIDER=network`: real SP1 proving via the Succinct Prover Network.
181181
Proving keys are set up per prestate on first use, and the aggregation
182182
verifying key must hash to the on-chain prestate (mismatches poison the
183183
prestate and remove its games from the owned set). The registered prestate's
184184
keys are verified BEFORE any game is created on it, so the proposer never
185185
bonds a game it has not proven it can defend.
186-
- `PROOF_PROVIDER=mock`: dev-only. Runs the full pipeline natively (witness
186+
- `KONA_SP1_PROPOSER_PROOF_PROVIDER=mock`: dev-only. Runs the full pipeline natively (witness
187187
collection computes the real range/consolidation outputs and the aggregation
188188
inputs are validated), then submits placeholder proof bytes. Only a deployment
189189
with a mock game verifier (devstack) accepts them. No ELFs, no SPN credentials.
@@ -212,41 +212,84 @@ slot (blocking a later defense of that game): watch
212212

213213
### Environment
214214

215-
Required:
215+
All proposer-owned variables use the `KONA_SP1_PROPOSER_` prefix.
216+
217+
Required core configuration:
218+
219+
| Variable | Purpose |
220+
|---|---|
221+
| `KONA_SP1_PROPOSER_L1_RPC` | L1 execution RPC |
222+
| `KONA_SP1_PROPOSER_SUPERROOT_RPC` | op-supernode or single-chain op-node RPC serving `superroot_atTimestamp` |
223+
| `KONA_SP1_PROPOSER_FACTORY_ADDRESS` | `DisputeGameFactory` address |
224+
| `KONA_SP1_PROPOSER_PRESTATES_URL` | prestate artifact directory (`<vkey>.agg.bin.gz` + `<vkey>.range.bin.gz`) |
225+
| `KONA_SP1_PROPOSER_PROOF_PROVIDER` | `network` or `mock`; no default |
226+
| `KONA_SP1_PROPOSER_L1_BEACON_RPC` | L1 beacon API (blob sidecars for derivation witnesses) |
227+
| `KONA_SP1_PROPOSER_L2_RPCS` | comma-separated L2 EL RPCs, one per chain (order-irrelevant) |
228+
229+
Optional core and operational configuration:
230+
231+
| Variable | Purpose |
232+
|---|---|
233+
| `KONA_SP1_PROPOSER_ROLLUP_CONFIG_PATHS` | comma-separated rollup config files; absent = registry fallback |
234+
| `KONA_SP1_PROPOSER_L1_CONFIG_PATH` | L1 chain config file; absent = registry fallback |
235+
| `KONA_SP1_PROPOSER_DEPENDENCY_SET_PATH` | dependency-set config file; absent = registry fallback |
236+
| `KONA_SP1_PROPOSER_PROPOSAL_INTERVAL_SECONDS` | proposal interval (default `3600`) |
237+
| `KONA_SP1_PROPOSER_PROPOSAL_SAFETY` | `safe` or `finalized` (default `finalized`) |
238+
| `KONA_SP1_PROPOSER_FETCH_INTERVAL` | loop interval in seconds (default `30`) |
239+
| `KONA_SP1_PROPOSER_METRICS_PORT` | `0` disables metrics; `auto` selects a free port (default `0`) |
240+
| `KONA_SP1_PROPOSER_SYNC_L1_CONFIRMATIONS` | L1 confirmation lag for pinned reads (default `0`) |
241+
| `KONA_SP1_PROPOSER_TX_CONFIRMATION_TIMEOUT` | transaction confirmation timeout in seconds (default `60`) |
242+
| `KONA_SP1_PROPOSER_MAX_FEE_PER_GAS` | L1 max-fee cap in wei (default uncapped) |
243+
| `KONA_SP1_PROPOSER_MAX_PRIORITY_FEE_PER_GAS` | L1 priority-fee cap in wei (default uncapped) |
244+
| `KONA_SP1_PROPOSER_RANGE_SPLIT_COUNT` | chunks per defended span (default `1`, maximum `16`) |
245+
| `KONA_SP1_PROPOSER_MAX_CONCURRENT_RANGE_PROOFS` | child-proof concurrency per game (default `1`) |
246+
| `KONA_SP1_PROPOSER_MAX_CONCURRENT_DEFENSE_TASKS` | concurrent defended games (default `8`, minimum `1`) |
247+
| `KONA_SP1_PROPOSER_FAST_FINALITY_MODE` | prove signer-created owned games while unchallenged (default `false`) |
248+
| `KONA_SP1_PROPOSER_FAST_FINALITY_PROVING_LIMIT` | total in-flight proving tasks before creation pauses (default `1`) |
249+
250+
SP1 network configuration applies when `KONA_SP1_PROPOSER_PROOF_PROVIDER=network`:
251+
252+
| Variable | Purpose |
253+
|---|---|
254+
| `KONA_SP1_PROPOSER_NETWORK_PRIVATE_KEY` | SPN requester private key, or AWS KMS key ARN when KMS is enabled |
255+
| `KONA_SP1_PROPOSER_NETWORK_RPC_URL` | SPN RPC override; absent or empty uses the SP1 SDK default for the selected network mode |
256+
| `KONA_SP1_PROPOSER_USE_KMS_REQUESTER` | use AWS KMS for request signing (default `false`) |
257+
| `KONA_SP1_PROPOSER_RANGE_PROOF_STRATEGY` | range fulfillment strategy (default `reserved`) |
258+
| `KONA_SP1_PROPOSER_AGG_PROOF_STRATEGY` | aggregation fulfillment strategy (default `reserved`) |
259+
| `KONA_SP1_PROPOSER_SP1_TIMEOUT_SECONDS` | overall proof timeout (default `14400`) |
260+
| `KONA_SP1_PROPOSER_NETWORK_CALLS_TIMEOUT` | individual network-call timeout (default `15`) |
261+
| `KONA_SP1_PROPOSER_AUCTION_TIMEOUT` | unassigned mainnet request timeout (default `60`) |
262+
| `KONA_SP1_PROPOSER_RANGE_CYCLE_LIMIT` | range request cycle limit (default `1e12`) |
263+
| `KONA_SP1_PROPOSER_RANGE_GAS_LIMIT` | range request gas limit (default `1e12`) |
264+
| `KONA_SP1_PROPOSER_AGG_CYCLE_LIMIT` | aggregation request cycle limit (default `1e12`) |
265+
| `KONA_SP1_PROPOSER_AGG_GAS_LIMIT` | aggregation request gas limit (default `1e12`) |
266+
| `KONA_SP1_PROPOSER_MAX_PRICE_PER_PGU` | maximum price per proving gas unit (default `3e8`) |
267+
| `KONA_SP1_PROPOSER_MIN_AUCTION_PERIOD` | minimum auction period in seconds (default `1`) |
268+
269+
Transaction signing requires one of these configurations:
216270

217271
| Variable | Purpose |
218272
|---|---|
219-
| `L1_RPC` | L1 execution RPC |
220-
| `SUPERROOT_RPC` | op-supernode or single-chain op-node RPC serving `superroot_atTimestamp` |
221-
| `FACTORY_ADDRESS` | `DisputeGameFactory` address |
222-
| `PRESTATES_URL` | prestate artifact directory (`<vkey>.agg.bin.gz` + `<vkey>.range.bin.gz`) |
223-
| `PROOF_PROVIDER` | `network` or `mock`; no default |
224-
| `L1_BEACON_RPC` | L1 beacon API (blob sidecars for derivation witnesses) |
225-
| `L2_RPCS` | comma-separated L2 EL RPCs, one per chain (order-irrelevant) |
226-
| `PRIVATE_KEY` or `SIGNER_URL`+`SIGNER_ADDRESS` | L1 transaction signer |
273+
| `KONA_SP1_PROPOSER_PRIVATE_KEY` | local L1 transaction-signing key |
274+
| `KONA_SP1_PROPOSER_SIGNER_URL` | Web3Signer URL; requires `KONA_SP1_PROPOSER_SIGNER_ADDRESS` |
275+
| `KONA_SP1_PROPOSER_SIGNER_ADDRESS` | Web3Signer address; requires `KONA_SP1_PROPOSER_SIGNER_URL` |
227276

228-
Optional (defaults in parentheses):
277+
Logging and telemetry:
229278

230279
| Variable | Purpose |
231280
|---|---|
232-
| `ROLLUP_CONFIG_PATHS`, `L1_CONFIG_PATH`, `DEPENDENCY_SET_PATH` | chain config files; absent = superchain-registry fallback, matching the executor CLI |
233-
| `PROPOSAL_INTERVAL_SECONDS` (3600), `PROPOSAL_SAFETY` (finalized), `FETCH_INTERVAL` (30) | proposal cadence |
234-
| `METRICS_PORT` (0 = disabled, `auto` = a free port reported on the startup line), `SYNC_L1_CONFIRMATIONS` (0), `TX_CONFIRMATION_TIMEOUT` (60) | operations |
235-
| `MAX_FEE_PER_GAS`, `MAX_PRIORITY_FEE_PER_GAS` | L1 fee caps in wei (unset = uncapped) |
236-
| `RANGE_SPLIT_COUNT` (1, max 16) | chunks a defended span is split into |
237-
| `MAX_CONCURRENT_RANGE_PROOFS` (1) | child-proof concurrency within one game |
238-
| `MAX_CONCURRENT_DEFENSE_TASKS` (8) | games defended concurrently (must be >= 1) |
239-
| `FAST_FINALITY_MODE` (false) | prove signer-created owned games while unchallenged |
240-
| `FAST_FINALITY_PROVING_LIMIT` (1) | total in-flight proving tasks (defense included) before creation pauses |
241-
| `NETWORK_PRIVATE_KEY` (network mode; `USE_KMS_REQUESTER` for AWS KMS) | SPN requester key |
242-
| `RANGE_PROOF_STRATEGY`, `AGG_PROOF_STRATEGY` (reserved) | SPN fulfillment strategies |
243-
| `SP1_TIMEOUT_SECONDS` (14400), `NETWORK_CALLS_TIMEOUT` (15), `AUCTION_TIMEOUT` (60) | SPN timeouts |
244-
| `RANGE_CYCLE_LIMIT`, `RANGE_GAS_LIMIT`, `AGG_CYCLE_LIMIT`, `AGG_GAS_LIMIT` (1e12) | SPN request limits |
245-
| `MAX_PRICE_PER_PGU` (3e8), `MIN_AUCTION_PERIOD` (1) | SPN pricing |
281+
| `KONA_SP1_PROPOSER_LOGGER_NAME` | OpenTelemetry service name (default `kona-sp1`) |
282+
| `KONA_SP1_PROPOSER_OTLP_ENDPOINT` | OpenTelemetry endpoint (default `http://localhost:4317`) |
283+
| `KONA_SP1_PROPOSER_OTLP_ENABLED` | enable OpenTelemetry export (default `false`) |
284+
| `KONA_SP1_PROPOSER_LOG_FORMAT` | `pretty` or `json` (default `pretty`) |
285+
286+
The proposer and its dependencies also observe the standard `RUST_LOG`, `NO_COLOR`,
287+
`SSL_CERT_DIR`, `SSL_CERT_FILE`, `OTEL_*`, proxy, AWS credential, and SP1 worker/debug
288+
variables. `KONA_SP1_ELF_DIR` configures shared build/test infrastructure.
246289

247290
### Fast finality
248291

249-
With `FAST_FINALITY_MODE=true` the proposer proves every signer-created owned
292+
With `KONA_SP1_PROPOSER_FAST_FINALITY_MODE=true` the proposer proves every signer-created owned
250293
game while it is still unchallenged, spawned by the per-tick scan one fetch
251294
interval after creation. A proven game is over immediately, so it resolves as
252295
soon as its parent does instead of waiting out `maxChallengeDuration`: proof
@@ -256,7 +299,7 @@ Off by default.
256299
Spend framing: in network mode this proves every game created by this signer
257300
whose prestate artifacts are available. At a one-hour proposal interval that is
258301
a baseline of 24 aggregation proofs per day; the default
259-
`FAST_FINALITY_PROVING_LIMIT=1` serializes them. An unchallenged game created by
302+
`KONA_SP1_PROPOSER_FAST_FINALITY_PROVING_LIMIT=1` serializes them. An unchallenged game created by
260303
another proposer is not proven, even when it uses a known prestate. Enabling the
261304
mode on a chain with an existing unchallenged backlog proves the signer-created
262305
owned backlog.
@@ -267,7 +310,7 @@ Concurrency interaction:
267310
|---|---|
268311
| active proving tasks (defense + fast finality) >= limit | no new fast-finality proving; game creation paused this tick |
269312
| defense tasks alone >= limit | same: defense load pauses creation (upstream parity) |
270-
| fast-finality tasks in flight | never count against `MAX_CONCURRENT_DEFENSE_TASKS` |
313+
| fast-finality tasks in flight | never count against `KONA_SP1_PROPOSER_MAX_CONCURRENT_DEFENSE_TASKS` |
271314
| game challenged while a fast-finality proof is in flight | the proof stays valid; per-game dedup prevents a second task |
272315
| a fast-finality proof keeps failing at the limit | creation stays paused until it succeeds or is classified unprovable; watch `kona_sp1_proposer_game_proving_error` |
273316
## Building

‎rust/kona/sp1/crates/host/src/lib.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ pub mod metrics;
1212
pub mod network;
1313
pub mod witness_generation;
1414
pub use logger::setup_logger;
15+
16+
/// Builds an environment-variable name from a service prefix and suffix.
17+
pub fn prefixed_env_var(prefix: &str, suffix: &str) -> String {
18+
format!("{prefix}_{suffix}")
19+
}

‎rust/kona/sp1/crates/host/src/logger.rs‎

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use tracing_subscriber::{
1212
util::SubscriberInitExt,
1313
};
1414

15+
use crate::prefixed_env_var;
16+
1517
static INIT: OnceLock<Result<()>> = OnceLock::new();
1618

1719
fn build_env_filter() -> EnvFilter {
@@ -47,24 +49,25 @@ fn build_env_filter() -> EnvFilter {
4749

4850
/// Set up the logger with optional `OpenTelemetry` export.
4951
///
50-
/// # Environment Variables
51-
/// - `LOGGER_NAME`: Service name for opentelemetry logs (defaults to `kona-sp1`)
52-
/// - `OTLP_ENDPOINT`: `OpenTelemetry` endpoint (defaults to <http://localhost:4317>)
53-
/// - `OTLP_ENABLED`: Whether to enable `OpenTelemetry` export (defaults to false)
54-
/// - `RUST_LOG`: Standard Rust log level configuration
55-
/// - `LOG_FORMAT`: Output format (pretty or json, defaults to pretty)
56-
pub fn setup_logger() {
52+
/// # Environment variables
53+
/// - `<PREFIX>_LOGGER_NAME`: service name (default `kona-sp1`)
54+
/// - `<PREFIX>_OTLP_ENDPOINT`: export endpoint (default <http://localhost:4317>)
55+
/// - `<PREFIX>_OTLP_ENABLED`: enable export (default `false`)
56+
/// - `<PREFIX>_LOG_FORMAT`: `pretty` or `json` (default `pretty`)
57+
/// - `RUST_LOG`: standard Rust log filtering
58+
/// - `NO_COLOR`: disable ANSI output
59+
pub fn setup_logger(prefix: &str) {
5760
INIT.get_or_init(|| {
58-
let logger_name = std::env::var("LOGGER_NAME").ok();
59-
let otlp_endpoint =
60-
std::env::var("OTLP_ENDPOINT").unwrap_or_else(|_| "http://localhost:4317".to_string());
61-
62-
let otlp_enabled = std::env::var("OTLP_ENABLED")
61+
let service_name = env::var(prefixed_env_var(prefix, "LOGGER_NAME"))
62+
.unwrap_or_else(|_| "kona-sp1".to_string());
63+
let otlp_endpoint = env::var(prefixed_env_var(prefix, "OTLP_ENDPOINT"))
64+
.unwrap_or_else(|_| "http://localhost:4317".to_string());
65+
let otlp_enabled = env::var(prefixed_env_var(prefix, "OTLP_ENABLED"))
6366
.unwrap_or_else(|_| "false".to_string())
6467
.parse::<bool>()
6568
.unwrap_or(false);
66-
67-
let service_name = logger_name.unwrap_or_else(|| "kona-sp1".to_string());
69+
let log_format = env::var(prefixed_env_var(prefix, "LOG_FORMAT"))
70+
.unwrap_or_else(|_| "pretty".to_string());
6871

6972
let params = vec![
7073
KeyValue::new("service.name", service_name),
@@ -74,7 +77,6 @@ pub fn setup_logger() {
7477
let resource = Resource::builder_empty().with_attributes(params).build();
7578
global::set_text_map_propagator(TraceContextPropagator::new());
7679

77-
let log_format = std::env::var("LOG_FORMAT").unwrap_or_else(|_| "pretty".to_string());
7880
let fmt_layer: Option<Box<dyn Layer<_> + Send + Sync>> =
7981
match log_format.to_lowercase().as_str() {
8082
"json" => {

0 commit comments

Comments
 (0)