Compare commits
3 Commits
95470a9178
...
b30957b0c0
Author | SHA1 | Date | |
---|---|---|---|
b30957b0c0 | |||
b78fac3d05 | |||
cb5e72921b |
23
.github/workflows/test.yml
vendored
Normal file
23
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: erlef/setup-beam@v1
|
||||||
|
with:
|
||||||
|
otp-version: "26.0.2"
|
||||||
|
gleam-version: "1.3.1"
|
||||||
|
rebar3-version: "3"
|
||||||
|
# elixir-version: "1.15.4"
|
||||||
|
- run: gleam deps download
|
||||||
|
- run: gleam test
|
||||||
|
- run: gleam format --check src test
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,3 +17,6 @@ deps
|
|||||||
_build/
|
_build/
|
||||||
_checkouts/
|
_checkouts/
|
||||||
|
|
||||||
|
*.beam
|
||||||
|
*.ez
|
||||||
|
/build
|
||||||
|
82
README.md
82
README.md
@ -1,3 +1,83 @@
|
|||||||
# pine
|
# pine
|
||||||
|
|
||||||
Configurable logger for gleam.
|
![](./pine.svg)
|
||||||
|
|
||||||
|
[![Package Version](https://img.shields.io/hexpm/v/pine)](https://hex.pm/packages/timber)
|
||||||
|
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/pine/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gleam add pine
|
||||||
|
```
|
||||||
|
|
||||||
|
```gleam
|
||||||
|
import pine
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
// The defaults work out of the box.
|
||||||
|
// This will print a line to the console.
|
||||||
|
pine.new()
|
||||||
|
|> pine.info("hello world!")
|
||||||
|
// info <unix millis> hello world!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Pine is configurable!
|
||||||
|
pine.new()
|
||||||
|
|
||||||
|
// You can set the level.
|
||||||
|
|> pine.set_level(pine.level_info)
|
||||||
|
|
||||||
|
// As well as the format.
|
||||||
|
|> pine.set_format(pine.format_json)
|
||||||
|
|
||||||
|
// And finally, the transport!
|
||||||
|
|> pine.set_transport(pine.transport_file("logs.txt"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Different log functions are on the pine module.
|
||||||
|
pine.new()
|
||||||
|
|
||||||
|
// You have access to "debug" logs
|
||||||
|
|> pine.debug("debug message")
|
||||||
|
|
||||||
|
// As well as 'info' logs
|
||||||
|
|> pine.info("info message")
|
||||||
|
|
||||||
|
// As well as 'warn' logs
|
||||||
|
|> pine.warn("warn message")
|
||||||
|
|
||||||
|
// As well as 'err' logs
|
||||||
|
|> pine.err("err message")
|
||||||
|
|
||||||
|
|
||||||
|
// You can add attributes to loggers as needed.
|
||||||
|
// This is especially useful for telemetry and tracing.
|
||||||
|
pine.new()
|
||||||
|
|
||||||
|
// Such as strings
|
||||||
|
|> pine.with_string("version", "v1.0.2")
|
||||||
|
|
||||||
|
// Or ints
|
||||||
|
|> pine.with_int("page_views", 69)
|
||||||
|
|
||||||
|
// Or floats
|
||||||
|
|> pine.with_float("opacity", 0.89)
|
||||||
|
|
||||||
|
// or bools
|
||||||
|
|> pine.with_bool("checked_balance", False)
|
||||||
|
|
||||||
|
// then see them all in action
|
||||||
|
|> pine.info("hello world!")
|
||||||
|
// info <unix millis> hello world! version=v1.0.2 page_views=69 opacity=0.89 checked_balance=false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Further documentation can be found at <https://hexdocs.pm/pine>.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gleam run # Run the project
|
||||||
|
gleam test # Run the tests
|
||||||
|
```
|
||||||
|
22
gleam.toml
Normal file
22
gleam.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name = "pine"
|
||||||
|
version = "0.0.2"
|
||||||
|
|
||||||
|
# Fill out these fields if you intend to generate HTML documentation or publish
|
||||||
|
# your project to the Hex package manager.
|
||||||
|
#
|
||||||
|
description = "configurable logger for gleam"
|
||||||
|
licences = ["MIT"]
|
||||||
|
# repository = { type = "github", user = "", repo = "" }
|
||||||
|
links = [{ title = "Git", href = "https://gitea.attum.co/wqtt/pine.git" }]
|
||||||
|
#
|
||||||
|
# For a full reference of all the available options, you can have a look at
|
||||||
|
# https://gleam.run/writing-gleam/gleam-toml/.
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
|
||||||
|
gleam_json = ">= 2.0.0 and < 3.0.0"
|
||||||
|
birl = ">= 1.7.1 and < 2.0.0"
|
||||||
|
simplifile = ">= 2.0.1 and < 3.0.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gleeunit = ">= 1.0.0 and < 2.0.0"
|
19
manifest.toml
Normal file
19
manifest.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# This file was generated by Gleam
|
||||||
|
# You typically do not need to edit this file
|
||||||
|
|
||||||
|
packages = [
|
||||||
|
{ name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" },
|
||||||
|
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
|
||||||
|
{ name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" },
|
||||||
|
{ name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" },
|
||||||
|
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
|
||||||
|
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
|
||||||
|
{ name = "simplifile", version = "2.0.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "5FFEBD0CAB39BDD343C3E1CCA6438B2848847DC170BA2386DF9D7064F34DF000" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[requirements]
|
||||||
|
birl = { version = ">= 1.7.1 and < 2.0.0" }
|
||||||
|
gleam_json = { version = ">= 2.0.0 and < 3.0.0" }
|
||||||
|
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
|
||||||
|
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
|
||||||
|
simplifile = { version = ">= 2.0.1 and < 3.0.0"}
|
7
pine.svg
Normal file
7
pine.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 511.846 511.846" xml:space="preserve" width="800px" height="800px" fill="#000000">
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<g id="SVGRepo_iconCarrier"> <rect x="202.603" y="426.872" style="fill:#965353;" width="106.65" height="84.974"/> <polygon style="fill:#8AC054;" points="447.861,447.863 63.985,447.863 255.927,106.632 "/> <polygon style="fill:#9ED36A;" points="394.532,277.26 117.297,277.26 255.927,0 "/> <polygon style="opacity:0.1;enable-background:new ;" points="389.816,344.676 152.833,289.912 159.956,277.26 351.89,277.26 "/> <rect x="202.603" y="447.862" style="opacity:0.2;fill:#FFFFFF;enable-background:new ;" width="21.337" height="63.98"/> </g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
150
src/pine.gleam
Normal file
150
src/pine.gleam
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import birl
|
||||||
|
import gleam/list
|
||||||
|
import gleam/order
|
||||||
|
import pine/attribute
|
||||||
|
import pine/format
|
||||||
|
import pine/level
|
||||||
|
import pine/log
|
||||||
|
import pine/transport
|
||||||
|
|
||||||
|
/// The logger type. This is opaque by default to prevent developers from relying
|
||||||
|
/// on implementation details that may change.
|
||||||
|
pub opaque type Logger {
|
||||||
|
Logger(
|
||||||
|
level: level.Level,
|
||||||
|
format: format.Formatter,
|
||||||
|
transport: transport.Transport,
|
||||||
|
attributes: List(attribute.Attribute),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new Logger.
|
||||||
|
///
|
||||||
|
/// This uses the defaults of:
|
||||||
|
/// - plain format
|
||||||
|
/// - console transport
|
||||||
|
/// - debug level
|
||||||
|
pub fn new() -> Logger {
|
||||||
|
Logger(level.debug, format.plain, transport.console, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a formatter that outputs to `json`.
|
||||||
|
pub fn format_json() -> format.Formatter {
|
||||||
|
format.json
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a formatter that outputs to one line of plain text where each attribute is separated by a space.
|
||||||
|
pub fn format_plain() -> format.Formatter {
|
||||||
|
format.plain
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a transport that writes the given file.
|
||||||
|
pub fn transport_file(filename) -> transport.Transport {
|
||||||
|
transport.file(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a transport that writes to the console.
|
||||||
|
pub fn transport_console() -> transport.Transport {
|
||||||
|
transport.console
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `debug` logging level.
|
||||||
|
pub fn level_debug() {
|
||||||
|
level.debug
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `info` logging level.
|
||||||
|
pub fn level_info() {
|
||||||
|
level.info
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `warn` logging level.
|
||||||
|
pub fn level_warn() {
|
||||||
|
level.warn
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `err` logging level.
|
||||||
|
pub fn level_err() {
|
||||||
|
level.err
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the logger to the given level.
|
||||||
|
pub fn set_level(logger: Logger, new_level: level.Level) -> Logger {
|
||||||
|
Logger(new_level, logger.format, logger.transport, logger.attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the logger to the given format.
|
||||||
|
pub fn set_format(logger: Logger, new_format: format.Formatter) -> Logger {
|
||||||
|
Logger(logger.level, new_format, logger.transport, logger.attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the logger to the given transport.
|
||||||
|
pub fn set_transport(
|
||||||
|
logger: Logger,
|
||||||
|
new_transport: transport.Transport,
|
||||||
|
) -> Logger {
|
||||||
|
Logger(logger.level, logger.format, new_transport, logger.attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_attribute(logger: Logger, attribute: attribute.Attribute) -> Logger {
|
||||||
|
Logger(
|
||||||
|
logger.level,
|
||||||
|
logger.format,
|
||||||
|
logger.transport,
|
||||||
|
list.append(logger.attributes, [attribute]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a debug log.
|
||||||
|
pub fn debug(logger: Logger, msg: String) -> Logger {
|
||||||
|
send_log(logger, level.debug, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an info log.
|
||||||
|
pub fn info(logger: Logger, msg: String) -> Logger {
|
||||||
|
send_log(logger, level.info, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a warn log.
|
||||||
|
pub fn warn(logger: Logger, msg: String) -> Logger {
|
||||||
|
send_log(logger, level.warn, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an err log.
|
||||||
|
pub fn err(logger: Logger, msg: String) -> Logger {
|
||||||
|
send_log(logger, level.err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_log(logger: Logger, lev: level.Level, msg: String) -> Logger {
|
||||||
|
case level.compare(logger.level, lev) {
|
||||||
|
order.Lt -> logger
|
||||||
|
_ -> {
|
||||||
|
let now = birl.to_unix_milli(birl.now())
|
||||||
|
|
||||||
|
log.Log(ts: now, level: lev, msg: msg, attributes: logger.attributes)
|
||||||
|
|> logger.format
|
||||||
|
|> logger.transport
|
||||||
|
|> fn(_) { logger }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new 'string' attribute to a logger.
|
||||||
|
pub fn with_string(logger: Logger, key: String, value: String) -> Logger {
|
||||||
|
add_attribute(logger, attribute.StringAttribute(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new 'int' attribute to a logger.
|
||||||
|
pub fn with_int(logger: Logger, key: String, value: Int) -> Logger {
|
||||||
|
add_attribute(logger, attribute.IntAttribute(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new 'float' attribute to a logger.
|
||||||
|
pub fn with_float(logger: Logger, key: String, value: Float) -> Logger {
|
||||||
|
add_attribute(logger, attribute.FloatAttribute(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new 'bool' attribute to a logger.
|
||||||
|
pub fn with_bool(logger: Logger, key: String, value: Bool) -> Logger {
|
||||||
|
add_attribute(logger, attribute.BoolAttribute(key, value))
|
||||||
|
}
|
6
src/pine/attribute.gleam
Normal file
6
src/pine/attribute.gleam
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub type Attribute {
|
||||||
|
StringAttribute(String, String)
|
||||||
|
IntAttribute(String, Int)
|
||||||
|
FloatAttribute(String, Float)
|
||||||
|
BoolAttribute(String, Bool)
|
||||||
|
}
|
68
src/pine/format.gleam
Normal file
68
src/pine/format.gleam
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import gleam/bool
|
||||||
|
import gleam/float
|
||||||
|
import gleam/int
|
||||||
|
import gleam/json as gleam_json
|
||||||
|
import gleam/list
|
||||||
|
import gleam/string
|
||||||
|
import pine/attribute
|
||||||
|
import pine/level
|
||||||
|
import pine/log
|
||||||
|
|
||||||
|
pub type Formatter =
|
||||||
|
fn(log.Log) -> String
|
||||||
|
|
||||||
|
pub fn plain(log: log.Log) -> String {
|
||||||
|
let extended_attributes =
|
||||||
|
list.fold(log.attributes, [], fn(acc, next) {
|
||||||
|
let attr = case next {
|
||||||
|
attribute.StringAttribute(key, value) -> key <> "=" <> value
|
||||||
|
|
||||||
|
attribute.IntAttribute(key, value) -> key <> "=" <> int.to_string(value)
|
||||||
|
|
||||||
|
attribute.FloatAttribute(key, value) ->
|
||||||
|
key <> "=" <> float.to_string(value)
|
||||||
|
|
||||||
|
attribute.BoolAttribute(key, value) ->
|
||||||
|
key <> "=" <> bool.to_string(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
list.append(acc, [attr])
|
||||||
|
})
|
||||||
|
|
||||||
|
string.join(
|
||||||
|
[
|
||||||
|
level.to_string(log.level),
|
||||||
|
int.to_string(log.ts),
|
||||||
|
log.msg,
|
||||||
|
..extended_attributes
|
||||||
|
],
|
||||||
|
" ",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn json(log: log.Log) -> String {
|
||||||
|
let default_attributes = [
|
||||||
|
#("ts", gleam_json.int(log.ts)),
|
||||||
|
#("level", gleam_json.string(level.to_string(log.level))),
|
||||||
|
#("msg", gleam_json.string(log.msg)),
|
||||||
|
]
|
||||||
|
|
||||||
|
let extended_attributes =
|
||||||
|
list.fold(log.attributes, [], fn(acc, next) {
|
||||||
|
let tup = case next {
|
||||||
|
attribute.StringAttribute(key, value) -> #(
|
||||||
|
key,
|
||||||
|
gleam_json.string(value),
|
||||||
|
)
|
||||||
|
attribute.IntAttribute(key, value) -> #(key, gleam_json.int(value))
|
||||||
|
attribute.FloatAttribute(key, value) -> #(key, gleam_json.float(value))
|
||||||
|
attribute.BoolAttribute(key, value) -> #(key, gleam_json.bool(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
list.append(acc, [tup])
|
||||||
|
})
|
||||||
|
|
||||||
|
let properties = list.append(default_attributes, extended_attributes)
|
||||||
|
|
||||||
|
gleam_json.to_string(gleam_json.object(properties))
|
||||||
|
}
|
63
src/pine/level.gleam
Normal file
63
src/pine/level.gleam
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import gleam/int
|
||||||
|
import gleam/order.{type Order}
|
||||||
|
|
||||||
|
/// Levels allow developers to categorize logs based on the type of information
|
||||||
|
/// the log contains. Levels can determine which logs are actually processed.
|
||||||
|
/// If the logger is configured with a _lower_ logging level than a particular
|
||||||
|
/// log uses, then that log will not be processed.
|
||||||
|
///
|
||||||
|
/// Levels are sequential in that each level contains the ones below it.
|
||||||
|
/// Eg, a logger configured with the `Debug` level will log every message because
|
||||||
|
/// `Debug` is the highest level. In contrast, a logger configured with the `Warn`
|
||||||
|
/// level will not display `Info` or `Debug` logs.
|
||||||
|
pub opaque type Level {
|
||||||
|
Debug
|
||||||
|
Info
|
||||||
|
Warn
|
||||||
|
Err
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const debug = Debug
|
||||||
|
|
||||||
|
pub const info = Info
|
||||||
|
|
||||||
|
pub const warn = Warn
|
||||||
|
|
||||||
|
pub const err = Err
|
||||||
|
|
||||||
|
/// Compare two levels.
|
||||||
|
pub fn compare(left: Level, right: Level) -> Order {
|
||||||
|
int.compare(to_int(left), to_int(right))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform an `Int` into it's corresonding log `Level`. Returns Ok if the int
|
||||||
|
/// is a valid log leve, otherwise Error(Nil).
|
||||||
|
pub fn from_int(level: Int) -> Result(Level, Nil) {
|
||||||
|
case level {
|
||||||
|
3 -> Ok(Debug)
|
||||||
|
2 -> Ok(Info)
|
||||||
|
1 -> Ok(Warn)
|
||||||
|
0 -> Ok(Err)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform a log `Level` into it's corresonding int value.
|
||||||
|
pub fn to_int(level: Level) -> Int {
|
||||||
|
case level {
|
||||||
|
Debug -> 3
|
||||||
|
Info -> 2
|
||||||
|
Warn -> 1
|
||||||
|
Err -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform a log `Level` into it's corresonding int value.
|
||||||
|
pub fn to_string(level: Level) -> String {
|
||||||
|
case level {
|
||||||
|
Debug -> "debug"
|
||||||
|
Info -> "info"
|
||||||
|
Warn -> "warn"
|
||||||
|
Err -> "err"
|
||||||
|
}
|
||||||
|
}
|
6
src/pine/log.gleam
Normal file
6
src/pine/log.gleam
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import pine/attribute.{type Attribute}
|
||||||
|
import pine/level.{type Level}
|
||||||
|
|
||||||
|
pub type Log {
|
||||||
|
Log(level: Level, ts: Int, msg: String, attributes: List(Attribute))
|
||||||
|
}
|
16
src/pine/transport.gleam
Normal file
16
src/pine/transport.gleam
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import gleam/io
|
||||||
|
import simplifile
|
||||||
|
|
||||||
|
pub type Transport =
|
||||||
|
fn(String) -> Nil
|
||||||
|
|
||||||
|
pub fn console(payload) {
|
||||||
|
io.println(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file(filename: String) {
|
||||||
|
fn(payload) {
|
||||||
|
let _ = simplifile.append(filename, payload)
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
11
test/timber_test.gleam
Normal file
11
test/timber_test.gleam
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import gleeunit
|
||||||
|
import gleeunit/should
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
gleeunit.main()
|
||||||
|
}
|
||||||
|
|
||||||
|
// gleeunit test functions end in `_test`
|
||||||
|
pub fn hello_world_test() {
|
||||||
|
should.equal(1, 1)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user