itsi-scheduler 0.1.5 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +120 -52
  3. data/README.md +57 -24
  4. data/Rakefile +0 -4
  5. data/ext/itsi_acme/Cargo.toml +86 -0
  6. data/ext/itsi_acme/examples/high_level.rs +63 -0
  7. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  8. data/ext/itsi_acme/examples/low_level.rs +87 -0
  9. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  10. data/ext/itsi_acme/src/acceptor.rs +81 -0
  11. data/ext/itsi_acme/src/acme.rs +354 -0
  12. data/ext/itsi_acme/src/axum.rs +86 -0
  13. data/ext/itsi_acme/src/cache.rs +39 -0
  14. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  15. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  16. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  17. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  18. data/ext/itsi_acme/src/caches/no.rs +78 -0
  19. data/ext/itsi_acme/src/caches/test.rs +136 -0
  20. data/ext/itsi_acme/src/config.rs +172 -0
  21. data/ext/itsi_acme/src/https_helper.rs +69 -0
  22. data/ext/itsi_acme/src/incoming.rs +142 -0
  23. data/ext/itsi_acme/src/jose.rs +161 -0
  24. data/ext/itsi_acme/src/lib.rs +142 -0
  25. data/ext/itsi_acme/src/resolver.rs +59 -0
  26. data/ext/itsi_acme/src/state.rs +424 -0
  27. data/ext/itsi_error/Cargo.toml +1 -0
  28. data/ext/itsi_error/src/lib.rs +106 -7
  29. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  30. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  31. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  32. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  33. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  34. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  35. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  36. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  37. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  38. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  39. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  40. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  41. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  42. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  43. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  44. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  45. data/ext/itsi_rb_helpers/src/lib.rs +63 -12
  46. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  47. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  48. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  49. data/ext/itsi_rb_helpers/target/debug/build/rb-sys-eb9ed4ff3a60f995/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  50. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  51. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  52. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  53. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  54. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  55. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  56. data/ext/itsi_scheduler/Cargo.toml +1 -1
  57. data/ext/itsi_scheduler/src/itsi_scheduler.rs +9 -3
  58. data/ext/itsi_scheduler/src/lib.rs +1 -0
  59. data/ext/itsi_server/Cargo.lock +2956 -0
  60. data/ext/itsi_server/Cargo.toml +73 -29
  61. data/ext/itsi_server/src/default_responses/mod.rs +11 -0
  62. data/ext/itsi_server/src/env.rs +43 -0
  63. data/ext/itsi_server/src/lib.rs +114 -75
  64. data/ext/itsi_server/src/prelude.rs +2 -0
  65. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  66. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +29 -8
  67. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
  68. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +264 -0
  69. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +362 -0
  70. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +84 -40
  71. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +233 -0
  72. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +565 -0
  73. data/ext/itsi_server/src/ruby_types/itsi_server.rs +86 -0
  74. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  75. data/ext/itsi_server/src/server/{bind.rs → binds/bind.rs} +59 -24
  76. data/ext/itsi_server/src/server/binds/listener.rs +444 -0
  77. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  78. data/ext/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +57 -19
  79. data/ext/itsi_server/src/server/{tls.rs → binds/tls.rs} +120 -31
  80. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  81. data/ext/itsi_server/src/server/http_message_types.rs +97 -0
  82. data/ext/itsi_server/src/server/io_stream.rs +2 -1
  83. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +94 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +316 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +301 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +192 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +171 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +198 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  99. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  100. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  101. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +116 -0
  102. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +411 -0
  103. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +142 -0
  104. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +55 -0
  105. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  106. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  107. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +126 -0
  108. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +187 -0
  109. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
  110. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +173 -0
  111. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  112. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  113. data/ext/itsi_server/src/server/mod.rs +7 -5
  114. data/ext/itsi_server/src/server/process_worker.rs +65 -14
  115. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  116. data/ext/itsi_server/src/server/request_job.rs +11 -0
  117. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +150 -50
  118. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  119. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +399 -165
  120. data/ext/itsi_server/src/server/signal.rs +33 -26
  121. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  122. data/ext/itsi_server/src/server/thread_worker.rs +218 -107
  123. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  124. data/ext/itsi_server/src/services/itsi_http_service.rs +257 -0
  125. data/ext/itsi_server/src/services/mime_types.rs +1416 -0
  126. data/ext/itsi_server/src/services/mod.rs +6 -0
  127. data/ext/itsi_server/src/services/password_hasher.rs +83 -0
  128. data/ext/itsi_server/src/services/rate_limiter.rs +580 -0
  129. data/ext/itsi_server/src/services/static_file_server.rs +1340 -0
  130. data/ext/itsi_tracing/Cargo.toml +1 -0
  131. data/ext/itsi_tracing/src/lib.rs +362 -33
  132. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  133. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  134. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  135. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  136. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  137. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  138. data/itsi-scheduler-100.png +0 -0
  139. data/lib/itsi/scheduler/version.rb +1 -1
  140. data/lib/itsi/scheduler.rb +11 -6
  141. metadata +117 -24
  142. data/CHANGELOG.md +0 -5
  143. data/CODE_OF_CONDUCT.md +0 -132
  144. data/LICENSE.txt +0 -21
  145. data/ext/itsi_error/src/from.rs +0 -71
  146. data/ext/itsi_server/extconf.rb +0 -6
  147. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  148. data/ext/itsi_server/src/request/itsi_request.rs +0 -277
  149. data/ext/itsi_server/src/request/mod.rs +0 -1
  150. data/ext/itsi_server/src/response/mod.rs +0 -1
  151. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -13
  152. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -5
  153. data/ext/itsi_server/src/server/itsi_server.rs +0 -244
  154. data/ext/itsi_server/src/server/listener.rs +0 -327
  155. /data/ext/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
@@ -14,3 +14,4 @@ tracing-subscriber = { version = "0.3.19", features = [
14
14
  ] }
15
15
  tracing-attributes = "0.1"
16
16
  atty = "0.2.14"
17
+ tracing-appender = "0.2.3"
@@ -1,41 +1,370 @@
1
- use std::env;
2
-
3
1
  use atty::{Stream, is};
2
+ use std::{
3
+ env,
4
+ sync::{Mutex, OnceLock},
5
+ };
6
+ use tracing::Level;
4
7
  pub use tracing::{debug, error, info, trace, warn};
5
- pub use tracing_attributes::instrument; // Explicitly export from tracing-attributes
6
- use tracing_subscriber::{
7
- EnvFilter,
8
- fmt::{self, format},
8
+ use tracing_appender::rolling;
9
+ use tracing_subscriber::fmt::{
10
+ format::{FmtSpan, JsonFields},
11
+ writer::BoxMakeWriter,
9
12
  };
13
+ use tracing_subscriber::{EnvFilter, fmt, prelude::*, reload};
14
+ use tracing_subscriber::{Layer, Registry, layer::Layered};
15
+
16
+ // Global reload handle for changing the level at runtime.
17
+ static RELOAD_HANDLE: OnceLock<
18
+ Mutex<Option<reload::Handle<EnvFilter, tracing_subscriber::Registry>>>,
19
+ > = OnceLock::new();
20
+
21
+ // Global reload handle for changing the formatting layer (log target/format) at runtime.
22
+ type ReloadFmtHandle = reload::Handle<
23
+ Box<
24
+ dyn Layer<
25
+ tracing_subscriber::layer::Layered<
26
+ reload::Layer<EnvFilter, tracing_subscriber::Registry>,
27
+ tracing_subscriber::Registry,
28
+ >,
29
+ > + Send
30
+ + Sync,
31
+ >,
32
+ Layered<tracing_subscriber::reload::Layer<EnvFilter, Registry>, Registry>,
33
+ >;
34
+
35
+ static RELOAD_FMT_HANDLE: OnceLock<Mutex<Option<ReloadFmtHandle>>> = OnceLock::new();
36
+
37
+ // Global current log configuration for formatting options.
38
+ static CURRENT_CONFIG: OnceLock<Mutex<LogConfig>> = OnceLock::new();
39
+
40
+ /// Log format: Plain or JSON.
41
+ #[derive(Debug, Clone)]
42
+ pub enum LogFormat {
43
+ Plain,
44
+ Json,
45
+ }
46
+
47
+ /// Log target: STDOUT, File, or Both.
48
+ #[derive(Debug, Clone)]
49
+ pub enum LogTarget {
50
+ Stdout,
51
+ File(String), // file name (rotated daily)
52
+ Both(String), // file name (rotated daily) plus STDOUT
53
+ }
54
+
55
+ /// Logger configuration.
56
+ #[derive(Debug, Clone)]
57
+ pub struct LogConfig {
58
+ /// Log level as a string (e.g. "info", "debug").
59
+ pub level: String,
60
+ /// Format: Plain (with optional ANSI) or JSON.
61
+ pub format: LogFormat,
62
+ /// Target: STDOUT, File, or Both.
63
+ pub target: LogTarget,
64
+ /// Whether to enable ANSI coloring (for plain text).
65
+ pub use_ansi: bool,
66
+ }
67
+
68
+ fn default_log_file() -> String {
69
+ env::var("ITSI_LOG_FILE").unwrap_or_else(|_| "itsi-app.log".into())
70
+ }
71
+
72
+ impl Default for LogConfig {
73
+ fn default() -> Self {
74
+ let level = env::var("ITSI_LOG").unwrap_or_else(|_| "info".into());
75
+ let format = match env::var("ITSI_LOG_FORMAT").as_deref() {
76
+ Ok("json") => LogFormat::Json,
77
+ _ => LogFormat::Plain,
78
+ };
79
+ let target = match env::var("ITSI_LOG_TARGET").as_deref() {
80
+ Ok("file") => LogTarget::File(default_log_file()),
81
+ Ok("both") => LogTarget::Both(default_log_file()),
82
+ _ => LogTarget::Stdout,
83
+ };
84
+ // If ITSI_LOG_ANSI is set, use that; otherwise, use ANSI if stdout is a TTY.
85
+ let use_ansi = env::var("ITSI_LOG_ANSI")
86
+ .map(|s| s == "true")
87
+ .unwrap_or_else(|_| is(Stream::Stdout));
88
+ Self {
89
+ level,
90
+ format,
91
+ target,
92
+ use_ansi,
93
+ }
94
+ }
95
+ }
96
+
97
+ /// Build the formatting layer based on the provided configuration.
98
+ fn build_fmt_layer(
99
+ config: &LogConfig,
100
+ ) -> Box<
101
+ dyn Layer<
102
+ tracing_subscriber::layer::Layered<
103
+ reload::Layer<EnvFilter, tracing_subscriber::Registry>,
104
+ tracing_subscriber::Registry,
105
+ >,
106
+ > + Send
107
+ + Sync,
108
+ > {
109
+ match &config.target {
110
+ LogTarget::Stdout => match config.format {
111
+ LogFormat::Plain => fmt::layer()
112
+ .compact()
113
+ .with_file(false)
114
+ .with_line_number(false)
115
+ .with_target(true)
116
+ .with_thread_ids(false)
117
+ .with_writer(BoxMakeWriter::new(std::io::stdout))
118
+ .with_ansi(config.use_ansi)
119
+ .with_span_events(FmtSpan::NONE)
120
+ .fmt_fields(JsonFields::new())
121
+ .boxed(),
122
+ LogFormat::Json => {
123
+ fmt::layer()
124
+ .compact()
125
+ .with_file(false)
126
+ .with_line_number(false)
127
+ .with_target(true)
128
+ .with_thread_ids(false)
129
+ .with_writer(BoxMakeWriter::new(std::io::stdout))
130
+ .with_ansi(config.use_ansi)
131
+ .event_format(fmt::format().json()) // set the JSON event formatter
132
+ .with_span_events(FmtSpan::NONE)
133
+ .fmt_fields(JsonFields::new())
134
+ .boxed()
135
+ }
136
+ },
137
+ LogTarget::File(file) => {
138
+ let file_clone = file.clone();
139
+ match config.format {
140
+ LogFormat::Plain => fmt::layer()
141
+ .compact()
142
+ .with_file(false)
143
+ .with_line_number(false)
144
+ .with_target(true)
145
+ .with_thread_ids(false)
146
+ .with_writer(BoxMakeWriter::new(move || {
147
+ rolling::daily(".", file_clone.clone())
148
+ }))
149
+ .with_ansi(false)
150
+ .with_span_events(FmtSpan::NONE)
151
+ .fmt_fields(JsonFields::new())
152
+ .boxed(),
153
+ LogFormat::Json => {
154
+ let file_clone = file.clone();
155
+ fmt::layer()
156
+ .compact()
157
+ .with_file(false)
158
+ .with_line_number(false)
159
+ .with_target(true)
160
+ .with_thread_ids(false)
161
+ .with_writer(BoxMakeWriter::new(move || {
162
+ rolling::daily(".", file_clone.clone())
163
+ }))
164
+ .with_ansi(false)
165
+ .event_format(fmt::format().json()) // set the JSON event formatter
166
+ .with_span_events(FmtSpan::NONE)
167
+ .fmt_fields(JsonFields::new())
168
+ .boxed()
169
+ }
170
+ }
171
+ }
172
+ LogTarget::Both(file) => {
173
+ let file_clone = file.clone();
174
+ match config.format {
175
+ LogFormat::Plain => {
176
+ let stdout_layer = fmt::layer()
177
+ .compact()
178
+ .with_file(false)
179
+ .with_line_number(false)
180
+ .with_target(true)
181
+ .with_thread_ids(false)
182
+ .with_writer(BoxMakeWriter::new(std::io::stdout))
183
+ .with_span_events(FmtSpan::NONE)
184
+ .fmt_fields(JsonFields::new())
185
+ .with_ansi(config.use_ansi);
186
+ let file_layer = fmt::layer()
187
+ .compact()
188
+ .with_file(false)
189
+ .with_line_number(false)
190
+ .with_target(true)
191
+ .with_thread_ids(false)
192
+ .with_writer(BoxMakeWriter::new(move || {
193
+ rolling::daily(".", file_clone.clone())
194
+ }))
195
+ .with_span_events(FmtSpan::NONE)
196
+ .fmt_fields(JsonFields::new())
197
+ .with_ansi(false);
198
+ stdout_layer.and_then(file_layer).boxed()
199
+ }
200
+ LogFormat::Json => {
201
+ let stdout_layer = fmt::layer()
202
+ .compact()
203
+ .with_file(false)
204
+ .with_line_number(false)
205
+ .with_target(true)
206
+ .with_thread_ids(false)
207
+ .with_writer(BoxMakeWriter::new(std::io::stdout))
208
+ .with_ansi(config.use_ansi)
209
+ .event_format(fmt::format().json()) // set the JSON event formatter
210
+ .with_span_events(FmtSpan::NONE)
211
+ .fmt_fields(JsonFields::new());
212
+ let file_layer = fmt::layer()
213
+ .compact()
214
+ .with_file(false)
215
+ .with_line_number(false)
216
+ .with_target(true)
217
+ .with_thread_ids(false)
218
+ .with_writer(BoxMakeWriter::new(move || {
219
+ rolling::daily(".", file_clone.clone())
220
+ }))
221
+ .with_ansi(false)
222
+ .event_format(fmt::format().json()) // set the JSON event formatter
223
+ .with_span_events(FmtSpan::NONE)
224
+ .fmt_fields(JsonFields::new());
225
+ stdout_layer.and_then(file_layer).boxed()
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
10
231
 
11
- #[instrument]
232
+ /// Update the formatting layer using the current configuration.
233
+ fn update_fmt_layer(config: &LogConfig) {
234
+ if let Some(handle) = RELOAD_FMT_HANDLE.get().unwrap().lock().unwrap().as_ref() {
235
+ let new_layer = build_fmt_layer(config);
236
+ handle
237
+ .modify(|layer| {
238
+ *layer = new_layer;
239
+ })
240
+ .expect("Failed to update formatting layer");
241
+ } else {
242
+ eprintln!("Reload handle for formatting layer not initialized; call init() first.");
243
+ }
244
+ }
245
+
246
+ /// Initialize the global tracing subscriber with the default configuration.
12
247
  pub fn init() {
13
- let env_filter = EnvFilter::builder()
14
- .with_env_var("ITSI_LOG")
15
- .try_from_env()
16
- .unwrap_or_else(|_| EnvFilter::new("info"));
17
-
18
- let format = fmt::format()
19
- .compact()
20
- .with_file(false)
21
- .with_level(true)
22
- .with_line_number(false)
23
- .with_source_location(false)
24
- .with_target(false)
25
- .with_thread_ids(false);
26
-
27
- let is_tty = is(Stream::Stdout);
28
-
29
- let subscriber = tracing_subscriber::fmt()
30
- .event_format(format)
31
- .with_env_filter(env_filter);
32
-
33
- if (is_tty && env::var("ITSI_LOG_PLAIN").is_err()) || env::var("ITSI_LOG_ANSI").is_ok() {
34
- subscriber.with_ansi(true).init();
248
+ init_with_config(LogConfig::default());
249
+ }
250
+
251
+ /// Initialize the global tracing subscriber with a given configuration.
252
+ pub fn init_with_config(config: LogConfig) {
253
+ // Store the current config in a global for future updates.
254
+ CURRENT_CONFIG.set(Mutex::new(config.clone())).ok();
255
+
256
+ // Build an EnvFilter from the configured level.
257
+ let env_filter = EnvFilter::new(config.clone().level);
258
+
259
+ // Build the formatting layer based on the configuration.
260
+ let fmt_layer = build_fmt_layer(&config);
261
+
262
+ // Create a reloadable filter layer so we can update the level at runtime.
263
+ let (filter_layer, filter_handle) = reload::Layer::new(env_filter);
264
+
265
+ // Create a reloadable formatting layer so we can update the target/format at runtime.
266
+ let (fmt_layer, fmt_handle) = reload::Layer::new(fmt_layer);
267
+
268
+ // Build the subscriber registry.
269
+ let subscriber = tracing_subscriber::registry()
270
+ .with(filter_layer)
271
+ .with(fmt_layer);
272
+
273
+ tracing::subscriber::set_global_default(subscriber)
274
+ .expect("Unable to set global tracing subscriber");
275
+
276
+ RELOAD_HANDLE.set(Mutex::new(Some(filter_handle))).unwrap();
277
+ RELOAD_FMT_HANDLE.set(Mutex::new(Some(fmt_handle))).ok();
278
+ }
279
+
280
+ /// Change the log level at runtime.
281
+ pub fn set_level(new_level: &str) {
282
+ if let Some(handle) = RELOAD_HANDLE.get().unwrap().lock().unwrap().as_ref() {
283
+ handle
284
+ .modify(|filter| *filter = EnvFilter::new(new_level))
285
+ .expect("Failed to update log level");
286
+
287
+ // Also update the stored config.
288
+ if let Some(config_mutex) = CURRENT_CONFIG.get() {
289
+ let mut config = config_mutex.lock().unwrap();
290
+ config.level = new_level.to_string();
291
+ }
292
+ } else {
293
+ eprintln!("Reload handle not initialized; call init() first.");
294
+ }
295
+ }
296
+
297
+ /// Change the log target at runtime.
298
+ pub fn set_target(new_target: &str) {
299
+ let target: LogTarget = match new_target {
300
+ "stdout" => LogTarget::Stdout,
301
+ "both" => LogTarget::Both(default_log_file()),
302
+ path => LogTarget::File(path.to_string()),
303
+ };
304
+ if let Some(config_mutex) = CURRENT_CONFIG.get() {
305
+ let mut config = config_mutex.lock().unwrap();
306
+ config.target = target;
307
+ update_fmt_layer(&config);
35
308
  } else {
36
- subscriber
37
- .fmt_fields(format::JsonFields::default())
38
- .event_format(fmt::format().json())
39
- .init();
309
+ eprintln!("Current configuration not initialized; call init() first.");
40
310
  }
41
311
  }
312
+
313
+ /// Change the log format at runtime.
314
+ pub fn set_format(new_format: &str) {
315
+ let format = match new_format {
316
+ "json" => LogFormat::Json,
317
+ "plain" => LogFormat::Plain,
318
+ _ => LogFormat::Json,
319
+ };
320
+ if let Some(config_mutex) = CURRENT_CONFIG.get() {
321
+ let mut config = config_mutex.lock().unwrap();
322
+ config.format = format;
323
+ update_fmt_layer(&config);
324
+ } else {
325
+ eprintln!("Current configuration not initialized; call init() first.");
326
+ }
327
+ }
328
+ pub fn set_target_filters(targets: Vec<(&str, Level)>) {
329
+ if let Some(reload_handle_mutex) = RELOAD_HANDLE.get() {
330
+ if let Ok(handle_guard) = reload_handle_mutex.lock() {
331
+ if let Some(handle) = handle_guard.as_ref() {
332
+ let mut new_filter = EnvFilter::new("");
333
+
334
+ if let Some(config_mutex) = CURRENT_CONFIG.get() {
335
+ if let Ok(config) = config_mutex.lock() {
336
+ if let Ok(directive) = config.level.parse() {
337
+ new_filter = new_filter.add_directive(directive);
338
+ }
339
+ }
340
+ }
341
+
342
+ for (target, level) in targets {
343
+ let directive_str = format!("{}={}", target, level);
344
+ if let Ok(directive) = directive_str.parse() {
345
+ new_filter = new_filter.add_directive(directive);
346
+ }
347
+ }
348
+
349
+ if let Err(e) = handle.modify(|filter| *filter = new_filter) {
350
+ eprintln!("Failed to update filter with target directives: {}", e);
351
+ }
352
+ }
353
+ }
354
+ } else {
355
+ eprintln!("Reload handle for filter not initialized; call init() first.");
356
+ }
357
+ }
358
+
359
+ /// Run a function silently by temporarily setting a no-op subscriber.
360
+ pub fn run_silently<F, R>(f: F) -> R
361
+ where
362
+ F: FnOnce() -> R,
363
+ {
364
+ let no_op_subscriber = tracing_subscriber::fmt()
365
+ .with_writer(std::io::sink)
366
+ .with_max_level(tracing_subscriber::filter::LevelFilter::OFF)
367
+ .finish();
368
+ let dispatch = tracing::Dispatch::new(no_op_subscriber);
369
+ tracing::dispatcher::with_default(&dispatch, f)
370
+ }
Binary file
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Scheduler
5
- VERSION = "0.1.5"
5
+ VERSION = "0.2.2"
6
6
  end
7
7
  end
@@ -44,7 +44,9 @@ module Itsi
44
44
  fiber = Fiber.current
45
45
  token = Scheduler.resume_token
46
46
  readiness = register_io_wait(io.fileno, events, duration, token)
47
- readiness || block(nil, duration, fiber, token)
47
+ readiness ||= block(nil, duration, fiber, token)
48
+ clear_timer(token)
49
+ readiness
48
50
  end
49
51
 
50
52
  def unblock(_blocker, fiber)
@@ -73,7 +75,7 @@ module Itsi
73
75
  fiber.resume
74
76
  end
75
77
  rescue StandardError => e
76
- warn "Failed to resume fiber #{fiber}: #{e.message}"
78
+ warn "Fiber #{fiber} terminated on exception: #{e.message}"
77
79
  end
78
80
 
79
81
  def resume_fiber_with_readiness((token, readiness))
@@ -81,7 +83,7 @@ module Itsi
81
83
  fiber.resume(readiness)
82
84
  end
83
85
  rescue StandardError => e
84
- warn "Failed to resume fiber #{fiber}: #{e.message}"
86
+ warn "Fiber #{fiber} terminated on exception: #{e.message}"
85
87
  end
86
88
 
87
89
  def resume_blocked(fiber)
@@ -130,9 +132,12 @@ module Itsi
130
132
  # Need to defer to Process::Status rather than our extension
131
133
  # as we don't have a means of creating our own Process::Status.
132
134
  def process_wait(pid, flags)
133
- Thread.new do
134
- Process::Status.wait(pid, flags)
135
- end.value
135
+ result = nil
136
+ thread = Thread.new do
137
+ result = Process::Status.wait(pid, flags)
138
+ end
139
+ thread.join
140
+ result
136
141
  end
137
142
 
138
143
  def closed?