mirror of
https://github.com/gechandesu/structlog.git
synced 2026-04-15 05:43:19 +03:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b423c161af | |||
| c6d53758b3 | |||
| a9a0e22d3a | |||
| f688b3cad5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,3 +22,5 @@ bin/
|
||||
# vweb and database
|
||||
*.db
|
||||
*.js
|
||||
|
||||
*.bak
|
||||
|
||||
@@ -19,8 +19,8 @@ fn main() {
|
||||
log.info().message('Hello, World!').send()
|
||||
|
||||
// You can set your own named fields.
|
||||
log.info().field('random_string', rand.string(5)).send()
|
||||
log.info().field('answer', 42).field('computed_by', 'Deep Thought').send()
|
||||
log.info().add('random_string', rand.string(100)).send()
|
||||
log.info().add('answer', 42).add('computed_by', 'Deep Thought').send()
|
||||
|
||||
// Errors can be passed to logger as is.
|
||||
log.error().message('this line contains error').error(error('oops')).send()
|
||||
|
||||
@@ -15,7 +15,7 @@ fn main() {
|
||||
}
|
||||
|
||||
log.info().message('Hello, World!').send()
|
||||
log.info().field('random_string', rand.string(5)).send()
|
||||
log.info().field('answer', 42).field('computed_by', 'Deep Thought').send()
|
||||
log.info().add('random_string', rand.string(100)).send()
|
||||
log.info().add('answer', 42).add('computed_by', 'Deep Thought').send()
|
||||
log.error().message('this line contains error').error(error('oops')).send()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import structlog
|
||||
fn main() {
|
||||
// Initialize logger with edited timestamp.
|
||||
log := structlog.new(
|
||||
// timestamp_format: .unix
|
||||
timestamp: structlog.Timestamp{
|
||||
format: .unix
|
||||
}
|
||||
|
||||
26
examples/write_to_file.v
Normal file
26
examples/write_to_file.v
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
import structlog
|
||||
|
||||
fn main() {
|
||||
// Open a file in append mode. If file does not exists it will be created.
|
||||
log_path := os.join_path_single(os.temp_dir(), 'example_log')
|
||||
log_file := os.open_file(log_path, 'a+') or {
|
||||
eprintln('Error: cound not open log file ${log_path}: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
eprintln('Log file location: ${log_path}')
|
||||
|
||||
// Initialize logger with os.File as writer.
|
||||
log := structlog.new(
|
||||
handler: structlog.TextHandler{
|
||||
color: false
|
||||
writer: log_file
|
||||
}
|
||||
)
|
||||
defer {
|
||||
log.close()
|
||||
}
|
||||
|
||||
log.info().message('Hello, World!').send()
|
||||
}
|
||||
141
structlog.v
141
structlog.v
@@ -96,9 +96,8 @@ pub fn (r Record) append(field ...Field) Record {
|
||||
mut fields_orig := unsafe { r.fields }
|
||||
fields_orig << field
|
||||
return Record{
|
||||
channel: r.channel
|
||||
level: r.level
|
||||
fields: &fields_orig
|
||||
...r
|
||||
fields: &fields_orig
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,21 +109,26 @@ pub fn (r Record) prepend(field ...Field) Record {
|
||||
mut new_fields := unsafe { field }
|
||||
new_fields << r.fields
|
||||
return Record{
|
||||
channel: r.channel
|
||||
level: r.level
|
||||
fields: new_fields
|
||||
...r
|
||||
fields: new_fields
|
||||
}
|
||||
}
|
||||
|
||||
// add adds new field with given name and value to a record and returns the modified record.
|
||||
pub fn (r Record) add(name string, value Value) Record {
|
||||
return r.append(field(name, value))
|
||||
}
|
||||
|
||||
// field adds new field with given name and value to a record and returns the modified record.
|
||||
@[deprecated: 'use add() instead']
|
||||
pub fn (r Record) field(name string, value Value) Record {
|
||||
return r.append(Field{ name: name, value: value })
|
||||
}
|
||||
|
||||
// message adds new message field to a record and returns the modified record.
|
||||
// This is a shothand for `field('message', 'message text')`.
|
||||
// This is a shothand for `add('message', 'message text')`.
|
||||
pub fn (r Record) message(s string) Record {
|
||||
return r.field('message', s)
|
||||
return r.add('message', s)
|
||||
}
|
||||
|
||||
// error adds an error as new field to a record and returns the modified record.
|
||||
@@ -146,9 +150,9 @@ pub fn (r Record) send() {
|
||||
|
||||
pub struct Timestamp {
|
||||
pub mut:
|
||||
// format sets the format of datettime for logs.
|
||||
// TimestampFormat values map 1-to-1 to the date formats provided by `time.Time`.
|
||||
// If .custom format is selected the timestamp_custom field must be set.
|
||||
// format sets the format of datetime in logs. TimestampFormat values
|
||||
// map 1-to-1 to the date formats provided by `time.Time`.
|
||||
// If .custom format is selected the `custom` field must be set.
|
||||
format TimestampFormat = .rfc3339
|
||||
|
||||
// custom sets the custom datetime string format if format is set to .custom.
|
||||
@@ -192,18 +196,6 @@ pub:
|
||||
add_level bool = true // if true add `level` field to all log records.
|
||||
add_timestamp bool = true // if true add `timestamp` field to all log records.
|
||||
|
||||
// timestamp_format sets the format of datettime for logs.
|
||||
// TimestampFormat values map 1-to-1 to the date formats provided by `time.Time`.
|
||||
// If .custom format is selected the timestamp_custom field must be set.
|
||||
timestamp_format TimestampFormat = .rfc3339 @[deprecated: 'use `timestamp` instead']
|
||||
|
||||
// timestamp_custom sets the custom datetime string format if timestapm_format is
|
||||
// set to .custom. See docs for Time.format_custom() fn from stadnard `time` module.
|
||||
timestamp_custom string @[deprecated: 'use `timestamp` instead']
|
||||
|
||||
// If timestamp_local is true the local time will be used instead of UTC.
|
||||
timestamp_local bool @[deprecated: 'use `timestamp` instead']
|
||||
|
||||
// handler holds a log record handler object which is used to process logs.
|
||||
handler RecordHandler = TextHandler{
|
||||
writer: os.stdout()
|
||||
@@ -261,20 +253,10 @@ pub fn new(config LogConfig) StructuredLog {
|
||||
|
||||
mut extra_fields := []Field{}
|
||||
|
||||
mut timestamp := logger.timestamp
|
||||
mut timestamp_old := Timestamp{
|
||||
format: logger.timestamp_format
|
||||
custom: logger.timestamp_custom
|
||||
local: logger.timestamp_local
|
||||
}
|
||||
if timestamp != timestamp_old {
|
||||
timestamp = timestamp_old
|
||||
}
|
||||
|
||||
if logger.add_timestamp {
|
||||
extra_fields << Field{
|
||||
name: 'timestamp'
|
||||
value: timestamp.as_value()
|
||||
value: logger.timestamp.as_value()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +357,9 @@ pub struct JSONHandler {
|
||||
pub fn (mut h JSONHandler) handle(rec Record) ! {
|
||||
str := json.encode[map[string]Value](rec.fields.as_map()) + '\n'
|
||||
h.writer.write(str.bytes())!
|
||||
if h.writer is os.File {
|
||||
h.writer.flush()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextHandler {
|
||||
@@ -435,7 +420,7 @@ pub fn (mut h TextHandler) handle(rec Record) ! {
|
||||
buf.write_byte(` `)
|
||||
buf.write_string(quote(v.str()))
|
||||
if j != field.value.len {
|
||||
buf.write_byte(` `)
|
||||
buf.write_string(', ')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -446,12 +431,19 @@ pub fn (mut h TextHandler) handle(rec Record) ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
if i != rec.fields.len {
|
||||
buf.write_byte(` `)
|
||||
if i + 1 != rec.fields.len {
|
||||
if i in [0, 1] {
|
||||
buf.write_byte(` `)
|
||||
} else {
|
||||
buf.write_string(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.write_byte(`\n`)
|
||||
h.writer.write(buf)!
|
||||
if h.writer is os.File {
|
||||
h.writer.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@[inline]
|
||||
@@ -464,3 +456,78 @@ fn quote(input string) string {
|
||||
}
|
||||
return "'" + input + "'"
|
||||
}
|
||||
|
||||
// struct_adapter generates the log fields list form a flat struct.
|
||||
// Supported struct field attrubutes:
|
||||
//
|
||||
// | Attribute | Meaning |
|
||||
// | ------------------- | ------------------------------------------ |
|
||||
// | `@[skip]` | Do not process field at all |
|
||||
// | `@[structlog: '-']` | Do not process field at all |
|
||||
// | `@[omitempty]` | Do not process field if it has empty value |
|
||||
//
|
||||
// Note: Nested struct fields are not supported.
|
||||
pub fn struct_adapter[T](s T) []Field {
|
||||
$if T !is $struct {
|
||||
$compile_error('structlog.struct_adapted: only struct types is accepted')
|
||||
}
|
||||
mut fields := []Field{}
|
||||
mut skip := false
|
||||
mut omitempty := false
|
||||
$for f in s.fields {
|
||||
skip = false
|
||||
for attr in f.attrs {
|
||||
if attr == 'skip' || (attr.starts_with('structlog: ')
|
||||
&& attr.all_after('structlog: ').trim('"\'') == '-') {
|
||||
skip = true
|
||||
}
|
||||
if attr == 'omitempty' {
|
||||
omitempty = true
|
||||
}
|
||||
}
|
||||
value := s.$(f.name)
|
||||
if omitempty {
|
||||
skip = check_is_empty(value) or { false }
|
||||
}
|
||||
if !skip {
|
||||
fields << field(f.name, value)
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
fn check_is_empty[T](val T) ?bool {
|
||||
$if val is string {
|
||||
if val == '' {
|
||||
return false
|
||||
}
|
||||
} $else $if val is $int || val is $float {
|
||||
if val == 0 {
|
||||
return false
|
||||
}
|
||||
} $else $if val is ?string {
|
||||
return val ? != ''
|
||||
} $else $if val is ?int {
|
||||
return val ? != 0
|
||||
} $else $if val is ?f64 || val is ?f32 {
|
||||
return val ? != 0.0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// field creates new `Field` with given name and value.
|
||||
// Map values will be transformed to `map[string]Value`.
|
||||
pub fn field[T](name string, value T) Field {
|
||||
$if value is $struct {
|
||||
$compile_error('structlog.field: cannot pass struct as field value')
|
||||
}
|
||||
$if value is $map {
|
||||
mut value_map := map[string]Value{}
|
||||
for k, v in value {
|
||||
value_map[k.str()] = Value(v)
|
||||
}
|
||||
return Field{name, value_map}
|
||||
} $else {
|
||||
return Field{name, value}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user