mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-26 01:42:19 +00:00
♻️ sync owner migration and facade thinning
This commit is contained in:
@@ -1,667 +0,0 @@
|
||||
///|
|
||||
pub(all) suberror ConfigError {
|
||||
InvalidConfig(String)
|
||||
}
|
||||
|
||||
///|
|
||||
pub(all) enum SinkKind {
|
||||
Console
|
||||
JsonConsole
|
||||
TextConsole
|
||||
File
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct TextFormatterConfig {
|
||||
show_timestamp : Bool
|
||||
show_level : Bool
|
||||
show_target : Bool
|
||||
show_fields : Bool
|
||||
separator : String
|
||||
field_separator : String
|
||||
template : String
|
||||
color_mode : ColorMode
|
||||
color_support : ColorSupport
|
||||
style_markup : StyleMarkupMode
|
||||
target_style_markup : StyleMarkupMode
|
||||
fields_style_markup : StyleMarkupMode
|
||||
style_tags : Map[String, TextStyle]
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn TextFormatterConfig::new(
|
||||
show_timestamp? : Bool = true,
|
||||
show_level? : Bool = true,
|
||||
show_target? : Bool = true,
|
||||
show_fields? : Bool = true,
|
||||
separator? : String = " ",
|
||||
field_separator? : String = " ",
|
||||
template? : String = "",
|
||||
color_mode? : ColorMode = ColorMode::Never,
|
||||
color_support? : ColorSupport = ColorSupport::TrueColor,
|
||||
style_markup? : StyleMarkupMode = StyleMarkupMode::Full,
|
||||
target_style_markup? : StyleMarkupMode = StyleMarkupMode::Disabled,
|
||||
fields_style_markup? : StyleMarkupMode = StyleMarkupMode::Disabled,
|
||||
style_tags? : Map[String, TextStyle] = {},
|
||||
) -> TextFormatterConfig {
|
||||
{
|
||||
show_timestamp,
|
||||
show_level,
|
||||
show_target,
|
||||
show_fields,
|
||||
separator,
|
||||
field_separator,
|
||||
template,
|
||||
color_mode,
|
||||
color_support,
|
||||
style_markup,
|
||||
target_style_markup,
|
||||
fields_style_markup,
|
||||
style_tags,
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn style_tag_registry_from_config(
|
||||
style_tags : Map[String, TextStyle],
|
||||
) -> StyleTagRegistry {
|
||||
let registry = style_tag_registry()
|
||||
for name, style in style_tags {
|
||||
ignore(registry.set_tag(name, style~))
|
||||
}
|
||||
registry
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn TextFormatterConfig::to_formatter(
|
||||
self : TextFormatterConfig,
|
||||
) -> TextFormatter {
|
||||
text_formatter(
|
||||
show_timestamp=self.show_timestamp,
|
||||
show_level=self.show_level,
|
||||
show_target=self.show_target,
|
||||
show_fields=self.show_fields,
|
||||
separator=self.separator,
|
||||
field_separator=self.field_separator,
|
||||
template=self.template,
|
||||
color_mode=self.color_mode,
|
||||
color_support=self.color_support,
|
||||
style_markup=self.style_markup,
|
||||
target_style_markup=self.target_style_markup,
|
||||
fields_style_markup=self.fields_style_markup,
|
||||
style_tags=if self.style_tags.length() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(style_tag_registry_from_config(self.style_tags))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct QueueConfig {
|
||||
max_pending : Int
|
||||
overflow : QueueOverflowPolicy
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn QueueConfig::new(
|
||||
max_pending : Int,
|
||||
overflow? : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
|
||||
) -> QueueConfig {
|
||||
{ max_pending, overflow }
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct SinkConfig {
|
||||
kind : SinkKind
|
||||
path : String
|
||||
append : Bool
|
||||
auto_flush : Bool
|
||||
rotation : FileRotation?
|
||||
text_formatter : TextFormatterConfig
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn SinkConfig::new(
|
||||
kind? : SinkKind = SinkKind::Console,
|
||||
path? : String = "",
|
||||
append? : Bool = true,
|
||||
auto_flush? : Bool = true,
|
||||
rotation? : FileRotation? = None,
|
||||
text_formatter? : TextFormatterConfig = default_text_formatter_config(),
|
||||
) -> SinkConfig {
|
||||
{ kind, path, append, auto_flush, rotation, text_formatter }
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct LoggerConfig {
|
||||
min_level : @core.Level
|
||||
target : String
|
||||
timestamp : Bool
|
||||
sink : SinkConfig
|
||||
queue : QueueConfig?
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn LoggerConfig::new(
|
||||
min_level? : @core.Level = @core.Level::Info,
|
||||
target? : String = "",
|
||||
timestamp? : Bool = false,
|
||||
sink? : SinkConfig = default_sink_config(),
|
||||
queue? : QueueConfig? = None,
|
||||
) -> LoggerConfig {
|
||||
{ min_level, target, timestamp, sink, queue }
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn default_text_formatter_config() -> TextFormatterConfig {
|
||||
TextFormatterConfig::new()
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn default_sink_config() -> SinkConfig {
|
||||
SinkConfig::new()
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn default_logger_config() -> LoggerConfig {
|
||||
LoggerConfig::new()
|
||||
}
|
||||
|
||||
///|
|
||||
fn expect_object(
|
||||
value : @json_parser.JsonValue,
|
||||
context : String,
|
||||
) -> Map[String, @json_parser.JsonValue] raise ConfigError {
|
||||
match value.as_object() {
|
||||
Some(obj) => obj
|
||||
None => raise ConfigError::InvalidConfig("Expected object at " + context)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
key : String,
|
||||
default? : String = "",
|
||||
) -> String raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => text
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_optional_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
key : String,
|
||||
) -> String? raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => None
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) => Some(text)
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_bool(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
key : String,
|
||||
default~ : Bool,
|
||||
) -> Bool raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_bool() {
|
||||
Some(flag) => flag
|
||||
None => raise ConfigError::InvalidConfig("Expected bool at key " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_int(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
key : String,
|
||||
default~ : Int,
|
||||
) -> Int raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => default
|
||||
Some(value) =>
|
||||
match value.as_number() {
|
||||
Some(number) => number.to_int()
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected number at key " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn get_optional_int64_string(
|
||||
obj : Map[String, @json_parser.JsonValue],
|
||||
key : String,
|
||||
) -> Int64? raise ConfigError {
|
||||
match obj.get(key) {
|
||||
None => None
|
||||
Some(value) =>
|
||||
match value.as_string() {
|
||||
Some(text) =>
|
||||
Some(
|
||||
@string.parse_int64(text) catch {
|
||||
_ =>
|
||||
raise ConfigError::InvalidConfig(
|
||||
"Expected int64 string at key " + key,
|
||||
)
|
||||
},
|
||||
)
|
||||
None =>
|
||||
raise ConfigError::InvalidConfig("Expected string at key " + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_level(name : String) -> @core.Level raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"TRACE" => @core.Level::Trace
|
||||
"DEBUG" => @core.Level::Debug
|
||||
"INFO" => @core.Level::Info
|
||||
"WARN" => @core.Level::Warn
|
||||
"ERROR" => @core.Level::Error
|
||||
_ => raise ConfigError::InvalidConfig("Unsupported level: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_overflow(name : String) -> QueueOverflowPolicy raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"DROPNEWEST" => QueueOverflowPolicy::DropNewest
|
||||
"DROPPOLDEST" => QueueOverflowPolicy::DropOldest
|
||||
"DROPOLDEST" => QueueOverflowPolicy::DropOldest
|
||||
_ =>
|
||||
raise ConfigError::InvalidConfig(
|
||||
"Unsupported queue overflow policy: " + name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_sink_kind(name : String) -> SinkKind raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"CONSOLE" => SinkKind::Console
|
||||
"JSON_CONSOLE" => SinkKind::JsonConsole
|
||||
"JSONCONSOLE" => SinkKind::JsonConsole
|
||||
"TEXT_CONSOLE" => SinkKind::TextConsole
|
||||
"TEXTCONSOLE" => SinkKind::TextConsole
|
||||
"FILE" => SinkKind::File
|
||||
_ => raise ConfigError::InvalidConfig("Unsupported sink kind: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_color_mode(name : String) -> ColorMode raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"NEVER" => ColorMode::Never
|
||||
"AUTO" => ColorMode::Auto
|
||||
"ALWAYS" => ColorMode::Always
|
||||
_ => raise ConfigError::InvalidConfig("Unsupported color mode: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_color_support(name : String) -> ColorSupport raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"BASIC" => ColorSupport::Basic
|
||||
"TRUECOLOR" => ColorSupport::TrueColor
|
||||
_ => raise ConfigError::InvalidConfig("Unsupported color support: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_style_markup_mode(name : String) -> StyleMarkupMode raise ConfigError {
|
||||
match name.to_upper() {
|
||||
"DISABLED" => StyleMarkupMode::Disabled
|
||||
"BUILTIN" => StyleMarkupMode::Builtin
|
||||
"FULL" => StyleMarkupMode::Full
|
||||
_ =>
|
||||
raise ConfigError::InvalidConfig("Unsupported style markup mode: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn sink_kind_label(kind : SinkKind) -> String {
|
||||
match kind {
|
||||
SinkKind::Console => "console"
|
||||
SinkKind::JsonConsole => "json_console"
|
||||
SinkKind::TextConsole => "text_console"
|
||||
SinkKind::File => "file"
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_text_formatter_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> TextFormatterConfig raise ConfigError {
|
||||
let obj = expect_object(value, "text_formatter")
|
||||
TextFormatterConfig::new(
|
||||
show_timestamp=get_bool(obj, "show_timestamp", default=true),
|
||||
show_level=get_bool(obj, "show_level", default=true),
|
||||
show_target=get_bool(obj, "show_target", default=true),
|
||||
show_fields=get_bool(obj, "show_fields", default=true),
|
||||
separator=get_string(obj, "separator", default=" "),
|
||||
field_separator=get_string(obj, "field_separator", default=" "),
|
||||
template=get_string(obj, "template", default=""),
|
||||
color_mode=parse_color_mode(get_string(obj, "color_mode", default="never")),
|
||||
color_support=parse_color_support(
|
||||
get_string(obj, "color_support", default="truecolor"),
|
||||
),
|
||||
style_markup=parse_style_markup_mode(
|
||||
get_string(obj, "style_markup", default="full"),
|
||||
),
|
||||
target_style_markup=parse_style_markup_mode(
|
||||
get_string(obj, "target_style_markup", default="disabled"),
|
||||
),
|
||||
fields_style_markup=parse_style_markup_mode(
|
||||
get_string(obj, "fields_style_markup", default="disabled"),
|
||||
),
|
||||
style_tags=match obj.get("style_tags") {
|
||||
None => {}
|
||||
Some(inner) => parse_style_tags_config(inner)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_text_style_config(
|
||||
value : @json_parser.JsonValue,
|
||||
context : String,
|
||||
) -> TextStyle raise ConfigError {
|
||||
let obj = expect_object(value, context)
|
||||
text_style(
|
||||
fg=get_optional_string(obj, "fg"),
|
||||
bg=get_optional_string(obj, "bg"),
|
||||
bold=get_bool(obj, "bold", default=false),
|
||||
dim=get_bool(obj, "dim", default=false),
|
||||
italic=get_bool(obj, "italic", default=false),
|
||||
underline=get_bool(obj, "underline", default=false),
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_style_tags_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> Map[String, TextStyle] raise ConfigError {
|
||||
let obj = expect_object(value, "text_formatter.style_tags")
|
||||
let style_tags : Map[String, TextStyle] = {}
|
||||
for name, item in obj {
|
||||
style_tags[name] = parse_text_style_config(
|
||||
item,
|
||||
"text_formatter.style_tags." + name,
|
||||
)
|
||||
}
|
||||
style_tags
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_queue_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> QueueConfig raise ConfigError {
|
||||
let obj = expect_object(value, "queue")
|
||||
QueueConfig::new(
|
||||
get_int(obj, "max_pending", default=0),
|
||||
overflow=parse_overflow(get_string(obj, "overflow", default="DropNewest")),
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_file_rotation_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> FileRotation raise ConfigError {
|
||||
let obj = expect_object(value, "sink.rotation")
|
||||
let max_backups = get_int(obj, "max_backups", default=1)
|
||||
match get_optional_int64_string(obj, "max_bytes_i64") {
|
||||
Some(max_bytes_i64) => file_rotation_i64(max_bytes_i64, max_backups~)
|
||||
None => file_rotation(get_int(obj, "max_bytes", default=1), max_backups~)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn parse_sink_config(
|
||||
value : @json_parser.JsonValue,
|
||||
) -> SinkConfig raise ConfigError {
|
||||
let obj = expect_object(value, "sink")
|
||||
let kind = parse_sink_kind(get_string(obj, "kind", default="console"))
|
||||
let formatter = match obj.get("text_formatter") {
|
||||
None => default_text_formatter_config()
|
||||
Some(inner) => parse_text_formatter_config(inner)
|
||||
}
|
||||
let path = get_string(obj, "path", default="")
|
||||
match kind {
|
||||
SinkKind::File =>
|
||||
if path == "" {
|
||||
raise ConfigError::InvalidConfig("File sink requires non-empty path")
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
SinkConfig::new(
|
||||
kind~,
|
||||
path~,
|
||||
append=get_bool(obj, "append", default=true),
|
||||
auto_flush=get_bool(obj, "auto_flush", default=true),
|
||||
rotation=match obj.get("rotation") {
|
||||
None => None
|
||||
Some(inner) => Some(parse_file_rotation_config(inner))
|
||||
},
|
||||
text_formatter=formatter,
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn parse_logger_config_text(
|
||||
input : String,
|
||||
) -> LoggerConfig raise ConfigError {
|
||||
let root = @json_parser.parse(input) catch {
|
||||
e => raise ConfigError::InvalidConfig("Invalid JSON: " + e.to_string())
|
||||
}
|
||||
let obj = expect_object(root, "root")
|
||||
LoggerConfig::new(
|
||||
min_level=parse_level(get_string(obj, "min_level", default="INFO")),
|
||||
target=get_string(obj, "target", default=""),
|
||||
timestamp=get_bool(obj, "timestamp", default=false),
|
||||
sink=match obj.get("sink") {
|
||||
None => default_sink_config()
|
||||
Some(value) => parse_sink_config(value)
|
||||
},
|
||||
queue=match obj.get("queue") {
|
||||
None => None
|
||||
Some(value) => Some(parse_queue_config(value))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
"max_pending": @json_parser.JsonValue::Number(queue.max_pending.to_double()),
|
||||
"overflow": @json_parser.JsonValue::String(
|
||||
match queue.overflow {
|
||||
QueueOverflowPolicy::DropNewest => "DropNewest"
|
||||
QueueOverflowPolicy::DropOldest => "DropOldest"
|
||||
},
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_queue_config(
|
||||
queue : QueueConfig,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = queue_config_to_json(queue)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn text_formatter_config_to_json(
|
||||
config : TextFormatterConfig,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"show_timestamp": @json_parser.JsonValue::Bool(config.show_timestamp),
|
||||
"show_level": @json_parser.JsonValue::Bool(config.show_level),
|
||||
"show_target": @json_parser.JsonValue::Bool(config.show_target),
|
||||
"show_fields": @json_parser.JsonValue::Bool(config.show_fields),
|
||||
"separator": @json_parser.JsonValue::String(config.separator),
|
||||
"field_separator": @json_parser.JsonValue::String(config.field_separator),
|
||||
"template": @json_parser.JsonValue::String(config.template),
|
||||
"color_mode": @json_parser.JsonValue::String(
|
||||
color_mode_label(config.color_mode),
|
||||
),
|
||||
"color_support": @json_parser.JsonValue::String(
|
||||
color_support_label(config.color_support),
|
||||
),
|
||||
"style_markup": @json_parser.JsonValue::String(
|
||||
style_markup_mode_label(config.style_markup),
|
||||
),
|
||||
"target_style_markup": @json_parser.JsonValue::String(
|
||||
style_markup_mode_label(config.target_style_markup),
|
||||
),
|
||||
"fields_style_markup": @json_parser.JsonValue::String(
|
||||
style_markup_mode_label(config.fields_style_markup),
|
||||
),
|
||||
}
|
||||
if config.style_tags.length() != 0 {
|
||||
obj["style_tags"] = style_tags_config_to_json(config.style_tags)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
fn text_style_config_to_json(style : TextStyle) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"bold": @json_parser.JsonValue::Bool(style.bold),
|
||||
"dim": @json_parser.JsonValue::Bool(style.dim),
|
||||
"italic": @json_parser.JsonValue::Bool(style.italic),
|
||||
"underline": @json_parser.JsonValue::Bool(style.underline),
|
||||
}
|
||||
match style.fg {
|
||||
Some(value) => obj["fg"] = @json_parser.JsonValue::String(value)
|
||||
None => ()
|
||||
}
|
||||
match style.bg {
|
||||
Some(value) => obj["bg"] = @json_parser.JsonValue::String(value)
|
||||
None => ()
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
fn style_tags_config_to_json(
|
||||
style_tags : Map[String, TextStyle],
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {}
|
||||
for name, style in style_tags {
|
||||
obj[name] = text_style_config_to_json(style)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_text_formatter_config(
|
||||
config : TextFormatterConfig,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = text_formatter_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_rotation_config_to_json(
|
||||
config : FileRotation,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"max_bytes": @json_parser.JsonValue::Number(config.max_bytes.to_double()),
|
||||
"max_backups": @json_parser.JsonValue::Number(
|
||||
config.max_backups.to_double(),
|
||||
),
|
||||
}
|
||||
match config.native_wide_max_bytes {
|
||||
Some(value) =>
|
||||
obj["max_bytes_i64"] = @json_parser.JsonValue::String(value.to_string())
|
||||
None => ()
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"kind": @json_parser.JsonValue::String(sink_kind_label(config.kind)),
|
||||
"path": @json_parser.JsonValue::String(config.path),
|
||||
"append": @json_parser.JsonValue::Bool(config.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(config.auto_flush),
|
||||
"text_formatter": text_formatter_config_to_json(config.text_formatter),
|
||||
}
|
||||
match config.rotation {
|
||||
None => ()
|
||||
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_sink_config(
|
||||
config : SinkConfig,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = sink_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"min_level": @json_parser.JsonValue::String(config.min_level.label()),
|
||||
"target": @json_parser.JsonValue::String(config.target),
|
||||
"timestamp": @json_parser.JsonValue::Bool(config.timestamp),
|
||||
"sink": sink_config_to_json(config.sink),
|
||||
}
|
||||
match config.queue {
|
||||
None => ()
|
||||
Some(queue) => obj["queue"] = queue_config_to_json(queue)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_logger_config(
|
||||
config : LoggerConfig,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = logger_config_to_json(config)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,17 @@ pub fn open_file_handle_internal(path : String, append : Bool) -> FileHandle? {
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_exists_internal(path : String) -> Bool {
|
||||
let raw = file_open_ffi(string_to_c_bytes(path), string_to_c_bytes("rb"))
|
||||
if file_is_null_ffi(raw) {
|
||||
false
|
||||
} else {
|
||||
ignore(file_close_ffi(raw))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn write_file_handle_internal(
|
||||
handle : FileHandle,
|
||||
|
||||
+10
-38
@@ -1,75 +1,47 @@
|
||||
///|
|
||||
pub type RecordPredicate = (@core.Record) -> Bool
|
||||
pub type RecordPredicate = @record_ops.RecordPredicate
|
||||
|
||||
///|
|
||||
pub fn level_at_least(min_level : @core.Level) -> RecordPredicate {
|
||||
fn(rec) { rec.level.priority() >= min_level.priority() }
|
||||
@record_ops.level_at_least(min_level)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn target_is(target : String) -> RecordPredicate {
|
||||
fn(rec) { rec.target == target }
|
||||
@record_ops.target_is(target)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn target_has_prefix(prefix : String) -> RecordPredicate {
|
||||
fn(rec) { rec.target.has_prefix(prefix) }
|
||||
@record_ops.target_has_prefix(prefix)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn message_contains(fragment : String) -> RecordPredicate {
|
||||
fn(rec) { rec.message.contains(fragment) }
|
||||
@record_ops.message_contains(fragment)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn has_field(key : String) -> RecordPredicate {
|
||||
fn(rec) {
|
||||
for field in rec.fields {
|
||||
if field.key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@record_ops.has_field(key)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn field_equals(key : String, value : String) -> RecordPredicate {
|
||||
fn(rec) {
|
||||
for field in rec.fields {
|
||||
if field.key == key && field.value == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@record_ops.field_equals(key, value)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn not_(predicate : RecordPredicate) -> RecordPredicate {
|
||||
fn(rec) { !predicate(rec) }
|
||||
@record_ops.not_(predicate)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn all_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
|
||||
fn(rec) {
|
||||
for predicate in predicates {
|
||||
if !predicate(rec) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
@record_ops.all_of(predicates)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn any_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
|
||||
fn(rec) {
|
||||
for predicate in predicates {
|
||||
if predicate(rec) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@record_ops.any_of(predicates)
|
||||
}
|
||||
|
||||
+33
-1062
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
"Nanaloveyuki/BitLogger/src/core",
|
||||
"maria/json_parser",
|
||||
"Nanaloveyuki/BitLogger/src/formatting",
|
||||
"Nanaloveyuki/BitLogger/src/record_ops",
|
||||
"moonbitlang/core/env",
|
||||
"moonbitlang/core/json",
|
||||
"moonbitlang/core/ref",
|
||||
|
||||
+8
-42
@@ -1,47 +1,29 @@
|
||||
///|
|
||||
pub type RecordPatch = (@core.Record) -> @core.Record
|
||||
pub type RecordPatch = @record_ops.RecordPatch
|
||||
|
||||
///|
|
||||
pub fn identity_patch() -> RecordPatch {
|
||||
fn(rec) { rec }
|
||||
@record_ops.identity_patch()
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn set_target(target : String) -> RecordPatch {
|
||||
fn(rec) { rec.with_target(target) }
|
||||
@record_ops.set_target(target)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn prefix_message(prefix : String) -> RecordPatch {
|
||||
fn(rec) { rec.with_message("\{prefix}\{rec.message}") }
|
||||
@record_ops.prefix_message(prefix)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn append_fields(extra_fields : Array[@core.Field]) -> RecordPatch {
|
||||
fn(rec) {
|
||||
if extra_fields.length() == 0 {
|
||||
rec
|
||||
} else if rec.fields.length() == 0 {
|
||||
rec.with_fields(extra_fields)
|
||||
} else {
|
||||
rec.with_fields(rec.fields + extra_fields)
|
||||
}
|
||||
}
|
||||
@record_ops.append_fields(extra_fields)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn redact_field(key : String, placeholder? : String = "***") -> RecordPatch {
|
||||
fn(rec) {
|
||||
rec.with_fields(
|
||||
rec.fields.map(fn(field) {
|
||||
if field.key == key {
|
||||
field.with_value(placeholder)
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
@record_ops.redact_field(key, placeholder~)
|
||||
}
|
||||
|
||||
///|
|
||||
@@ -49,26 +31,10 @@ pub fn redact_fields(
|
||||
keys : Array[String],
|
||||
placeholder? : String = "***",
|
||||
) -> RecordPatch {
|
||||
fn(rec) {
|
||||
rec.with_fields(
|
||||
rec.fields.map(fn(field) {
|
||||
if keys.contains(field.key) {
|
||||
field.with_value(placeholder)
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
@record_ops.redact_fields(keys, placeholder~)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn compose_patches(patches : Array[RecordPatch]) -> RecordPatch {
|
||||
fn(rec) {
|
||||
let mut current = rec
|
||||
for patch in patches {
|
||||
current = patch(current)
|
||||
}
|
||||
current
|
||||
}
|
||||
@record_ops.compose_patches(patches)
|
||||
}
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
///|
|
||||
pub struct RuntimeFileState {
|
||||
file : FileSinkState
|
||||
queued : Bool
|
||||
pending_count : Int
|
||||
dropped_count : Int
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn RuntimeFileState::new(
|
||||
file : FileSinkState,
|
||||
queued? : Bool = false,
|
||||
pending_count? : Int = 0,
|
||||
dropped_count? : Int = 0,
|
||||
) -> RuntimeFileState {
|
||||
{ file, queued, pending_count, dropped_count }
|
||||
}
|
||||
|
||||
///|
|
||||
fn file_sink_policy_to_json_value(
|
||||
policy : FileSinkPolicy,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"append": @json_parser.JsonValue::Bool(policy.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(policy.auto_flush),
|
||||
}
|
||||
match policy.rotation {
|
||||
None => obj["rotation"] = @json_parser.JsonValue::Null
|
||||
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_policy_to_json(
|
||||
policy : FileSinkPolicy,
|
||||
) -> @json_parser.JsonValue {
|
||||
file_sink_policy_to_json_value(policy)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_file_sink_policy(
|
||||
policy : FileSinkPolicy,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = file_sink_policy_to_json_value(policy)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
fn file_sink_state_to_json_value(
|
||||
state : FileSinkState,
|
||||
) -> @json_parser.JsonValue {
|
||||
let obj : Map[String, @json_parser.JsonValue] = {
|
||||
"path": @json_parser.JsonValue::String(state.path),
|
||||
"available": @json_parser.JsonValue::Bool(state.available),
|
||||
"append": @json_parser.JsonValue::Bool(state.append),
|
||||
"auto_flush": @json_parser.JsonValue::Bool(state.auto_flush),
|
||||
"open_failures": @json_parser.JsonValue::Number(
|
||||
state.open_failures.to_double(),
|
||||
),
|
||||
"write_failures": @json_parser.JsonValue::Number(
|
||||
state.write_failures.to_double(),
|
||||
),
|
||||
"flush_failures": @json_parser.JsonValue::Number(
|
||||
state.flush_failures.to_double(),
|
||||
),
|
||||
"rotation_failures": @json_parser.JsonValue::Number(
|
||||
state.rotation_failures.to_double(),
|
||||
),
|
||||
}
|
||||
match state.rotation {
|
||||
None => obj["rotation"] = @json_parser.JsonValue::Null
|
||||
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
|
||||
}
|
||||
@json_parser.JsonValue::Object(obj)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
|
||||
file_sink_state_to_json_value(state)
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_file_sink_state(
|
||||
state : FileSinkState,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = file_sink_state_to_json_value(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn runtime_file_state_to_json(
|
||||
state : RuntimeFileState,
|
||||
) -> @json_parser.JsonValue {
|
||||
@json_parser.JsonValue::Object({
|
||||
"file": file_sink_state_to_json_value(state.file),
|
||||
"queued": @json_parser.JsonValue::Bool(state.queued),
|
||||
"pending_count": @json_parser.JsonValue::Number(
|
||||
state.pending_count.to_double(),
|
||||
),
|
||||
"dropped_count": @json_parser.JsonValue::Number(
|
||||
state.dropped_count.to_double(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn stringify_runtime_file_state(
|
||||
state : RuntimeFileState,
|
||||
pretty? : Bool = false,
|
||||
) -> String {
|
||||
let value = runtime_file_state_to_json(state)
|
||||
if pretty {
|
||||
@json_parser.stringify_pretty(value, 2)
|
||||
} else {
|
||||
@json_parser.stringify(value)
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1 @@
|
||||
///|
|
||||
pub struct FileRotation {
|
||||
max_bytes : Int
|
||||
max_backups : Int
|
||||
native_wide_max_bytes : Int64?
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct FileSinkState {
|
||||
path : String
|
||||
available : Bool
|
||||
append : Bool
|
||||
auto_flush : Bool
|
||||
rotation : FileRotation?
|
||||
open_failures : Int
|
||||
write_failures : Int
|
||||
flush_failures : Int
|
||||
rotation_failures : Int
|
||||
}
|
||||
|
||||
///|
|
||||
pub struct FileSinkPolicy {
|
||||
append : Bool
|
||||
auto_flush : Bool
|
||||
rotation : FileRotation?
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn FileSinkPolicy::new(
|
||||
append? : Bool = true,
|
||||
auto_flush? : Bool = true,
|
||||
rotation? : FileRotation? = None,
|
||||
) -> FileSinkPolicy {
|
||||
{ append, auto_flush, rotation }
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn FileSinkState::new(
|
||||
path : String,
|
||||
available? : Bool = false,
|
||||
append? : Bool = true,
|
||||
auto_flush? : Bool = true,
|
||||
rotation? : FileRotation? = None,
|
||||
open_failures? : Int = 0,
|
||||
write_failures? : Int = 0,
|
||||
flush_failures? : Int = 0,
|
||||
rotation_failures? : Int = 0,
|
||||
) -> FileSinkState {
|
||||
{
|
||||
path,
|
||||
available,
|
||||
append,
|
||||
auto_flush,
|
||||
rotation,
|
||||
open_failures,
|
||||
write_failures,
|
||||
flush_failures,
|
||||
rotation_failures,
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_rotation(max_bytes : Int, max_backups? : Int = 1) -> FileRotation {
|
||||
{
|
||||
max_bytes: if max_bytes <= 0 {
|
||||
1
|
||||
} else {
|
||||
max_bytes
|
||||
},
|
||||
max_backups: if max_backups <= 0 {
|
||||
1
|
||||
} else {
|
||||
max_backups
|
||||
},
|
||||
native_wide_max_bytes: None,
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub fn file_rotation_i64(
|
||||
max_bytes : Int64,
|
||||
max_backups? : Int = 1,
|
||||
) -> FileRotation {
|
||||
let normalized = if max_bytes <= 0L { 1L } else { max_bytes }
|
||||
let clamped_max_bytes = if normalized > 2147483647L {
|
||||
2147483647
|
||||
} else {
|
||||
normalized.to_int()
|
||||
}
|
||||
{
|
||||
max_bytes: clamped_max_bytes,
|
||||
max_backups: if max_backups <= 0 {
|
||||
1
|
||||
} else {
|
||||
max_backups
|
||||
},
|
||||
native_wide_max_bytes: Some(normalized),
|
||||
}
|
||||
}
|
||||
|
||||
///|
|
||||
pub(all) enum QueueOverflowPolicy {
|
||||
DropNewest
|
||||
DropOldest
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user