🧚 A practical web framework for Gleam

Better logging!

Closes https://github.com/gleam-wisp/wisp/issues/56

+53 -53
+5
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 + ## v0.12.0 - 2024-02-17 4 + 5 + - The output format used by the logger has been improved. 6 + - Erlang SASL and supervisor logs are no longer emitted. 7 + 3 8 ## v0.11.0 - 2024-02-03 4 9 5 10 - Updated for simplifile v1.4 and replaced the deprecated `simplifile.is_file`
+2 -1
gleam.toml
··· 10 10 ] 11 11 12 12 [dependencies] 13 - exception = "~> 1.0" 13 + exception = "~> 2.0" 14 14 gleam_crypto = "~> 1.0" 15 15 gleam_erlang = "~> 0.21" 16 16 gleam_http = "~> 3.5" ··· 19 19 mist = "~> 0.13" 20 20 simplifile = "~> 1.4" 21 21 marceau = "~> 1.1" 22 + logging = "~> 1.0" 22 23 23 24 [dev-dependencies] 24 25 gleeunit = "~> 1.0"
+8 -6
manifest.toml
··· 2 2 # You typically do not need to edit this file 3 3 4 4 packages = [ 5 - { name = "exception", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "984401CFC95BCA87C391E36194D2B9E5B946467D44893FADB1CA4ACD4B7A29CE" }, 6 - { name = "gleam_crypto", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "DE1FC4E631CA374AB29CCAEAC043EE171B86114D7DC66DD483F0A93BF0C4C6FF" }, 5 + { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, 6 + { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, 7 7 { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, 8 8 { name = "gleam_http", version = "3.5.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "C2FC3322203B16F897C1818D9810F5DEFCE347F0751F3B44421E1261277A7373" }, 9 9 { name = "gleam_json", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" }, 10 10 { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, 11 - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, 11 + { name = "gleam_stdlib", version = "0.35.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5443EEB74708454B65650FEBBB1EF5175057D1DEC62AEA9D7C6D96F41DA79152" }, 12 12 { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, 13 - { name = "glisten", version = "0.10.2", build_tools = ["gleam"], requirements = ["gleam_otp", "gleam_erlang", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "461AE0EC3C2BDCC8B581A0CE07D49597A61226B410A3FE7E237EB924D0D18536" }, 13 + { name = "glisten", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "73BC09C8487C2FFC0963BFAB33ED2F0D636FDFA43B966E65C1251CBAB8458099" }, 14 + { name = "logging", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "82C112ED9B6C30C1772A6FE2613B94B13F62EA35F5869A2630D13948D297BD39" }, 14 15 { name = "marceau", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "1AAD727A30BE0F95562C3403BB9B27C823797AD90037714255EEBF617B1CDA81" }, 15 - { name = "mist", version = "0.17.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glisten", "gleam_http", "gleam_otp", "gleam_erlang"], otp_app = "mist", source = "hex", outer_checksum = "DA8ACEE52C1E4892A75181B3166A4876D8CBC69D555E4770250BC84C80F75524" }, 16 + { name = "mist", version = "0.17.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten"], otp_app = "mist", source = "hex", outer_checksum = "DA8ACEE52C1E4892A75181B3166A4876D8CBC69D555E4770250BC84C80F75524" }, 16 17 { name = "simplifile", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "AAFCF154F69B237D269FF2764890F61ABC4A7EF2A592D44D67627B99694539D9" }, 17 18 { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, 18 19 ] 19 20 20 21 [requirements] 21 - exception = { version = "~> 1.0" } 22 + exception = { version = "~> 2.0" } 22 23 gleam_crypto = { version = "~> 1.0" } 23 24 gleam_erlang = { version = "~> 0.21" } 24 25 gleam_http = { version = "~> 3.5" } 25 26 gleam_json = { version = "~> 0.6 or ~> 1.0" } 26 27 gleam_stdlib = { version = "~> 0.29 or ~> 1.0" } 27 28 gleeunit = { version = "~> 1.0" } 29 + logging = { version = "~> 1.0" } 28 30 marceau = { version = "~> 1.1" } 29 31 mist = { version = "~> 0.13" } 30 32 simplifile = { version = "~> 1.4" }
+37 -12
src/wisp.gleam
··· 2 2 import gleam/bytes_builder 3 3 import gleam/bit_array 4 4 import gleam/bool 5 + import gleam/dict.{type Dict} 5 6 import gleam/crypto 6 7 import gleam/dynamic.{type Dynamic} 7 8 import gleam/erlang ··· 12 13 type Response as HttpResponse, Response as HttpResponse, 13 14 } 14 15 import gleam/int 16 + import gleam/erlang/atom.{type Atom} 15 17 import gleam/json 16 18 import gleam/list 17 19 import gleam/option.{type Option} ··· 19 21 import gleam/string 20 22 import gleam/string_builder.{type StringBuilder} 21 23 import gleam/uri 24 + import logging 22 25 import marceau 23 26 import mist 24 27 import simplifile 25 - import wisp/internal/logger 26 28 27 29 // 28 30 // Running the server ··· 1260 1262 /// ``` 1261 1263 /// 1262 1264 pub fn rescue_crashes(handler: fn() -> Response) -> Response { 1263 - case erlang.rescue(handler) { 1265 + case exception.rescue(handler) { 1264 1266 Ok(response) -> response 1265 1267 Error(error) -> { 1266 - log_error(string.inspect(error)) 1268 + let #(kind, detail) = case error { 1269 + exception.Errored(detail) -> #(Errored, detail) 1270 + exception.Thrown(detail) -> #(Thrown, detail) 1271 + exception.Exited(detail) -> #(Exited, detail) 1272 + } 1273 + case dynamic.dict(atom.from_dynamic, Ok)(detail) { 1274 + Ok(details) -> { 1275 + let c = atom.create_from_string("class") 1276 + log_error_dict(dict.insert(details, c, dynamic.from(kind))) 1277 + Nil 1278 + } 1279 + Error(_) -> log_error(string.inspect(error)) 1280 + } 1267 1281 internal_server_error() 1268 1282 } 1269 1283 } 1284 + } 1285 + 1286 + type DoNotLeak 1287 + 1288 + @external(erlang, "logger", "error") 1289 + fn log_error_dict(o: Dict(Atom, Dynamic)) -> DoNotLeak 1290 + 1291 + type ErrorKind { 1292 + Errored 1293 + Thrown 1294 + Exited 1270 1295 } 1271 1296 1272 1297 // TODO: test, somehow. ··· 1485 1510 /// In future this function may be extended to change the output format. 1486 1511 /// 1487 1512 pub fn configure_logger() -> Nil { 1488 - logger.configure_logger() 1513 + logging.configure() 1489 1514 } 1490 1515 1491 1516 /// Log a message to the Erlang logger with the level of `emergency`. ··· 1495 1520 /// [1]: https://www.erlang.org/doc/man/logger 1496 1521 /// 1497 1522 pub fn log_emergency(message: String) -> Nil { 1498 - logger.log(logger.Emergency, message) 1523 + logging.log(logging.Emergency, message) 1499 1524 } 1500 1525 1501 1526 /// Log a message to the Erlang logger with the level of `alert`. ··· 1505 1530 /// [1]: https://www.erlang.org/doc/man/logger 1506 1531 /// 1507 1532 pub fn log_alert(message: String) -> Nil { 1508 - logger.log(logger.Alert, message) 1533 + logging.log(logging.Alert, message) 1509 1534 } 1510 1535 1511 1536 /// Log a message to the Erlang logger with the level of `critical`. ··· 1515 1540 /// [1]: https://www.erlang.org/doc/man/logger 1516 1541 /// 1517 1542 pub fn log_critical(message: String) -> Nil { 1518 - logger.log(logger.Critical, message) 1543 + logging.log(logging.Critical, message) 1519 1544 } 1520 1545 1521 1546 /// Log a message to the Erlang logger with the level of `error`. ··· 1525 1550 /// [1]: https://www.erlang.org/doc/man/logger 1526 1551 /// 1527 1552 pub fn log_error(message: String) -> Nil { 1528 - logger.log(logger.Error, message) 1553 + logging.log(logging.Error, message) 1529 1554 } 1530 1555 1531 1556 /// Log a message to the Erlang logger with the level of `warning`. ··· 1535 1560 /// [1]: https://www.erlang.org/doc/man/logger 1536 1561 /// 1537 1562 pub fn log_warning(message: String) -> Nil { 1538 - logger.log(logger.Warning, message) 1563 + logging.log(logging.Warning, message) 1539 1564 } 1540 1565 1541 1566 /// Log a message to the Erlang logger with the level of `notice`. ··· 1545 1570 /// [1]: https://www.erlang.org/doc/man/logger 1546 1571 /// 1547 1572 pub fn log_notice(message: String) -> Nil { 1548 - logger.log(logger.Notice, message) 1573 + logging.log(logging.Notice, message) 1549 1574 } 1550 1575 1551 1576 /// Log a message to the Erlang logger with the level of `info`. ··· 1555 1580 /// [1]: https://www.erlang.org/doc/man/logger 1556 1581 /// 1557 1582 pub fn log_info(message: String) -> Nil { 1558 - logger.log(logger.Info, message) 1583 + logging.log(logging.Info, message) 1559 1584 } 1560 1585 1561 1586 /// Log a message to the Erlang logger with the level of `debug`. ··· 1565 1590 /// [1]: https://www.erlang.org/doc/man/logger 1566 1591 /// 1567 1592 pub fn log_debug(message: String) -> Nil { 1568 - logger.log(logger.Debug, message) 1593 + logging.log(logging.Debug, message) 1569 1594 } 1570 1595 1571 1596 //
-34
src/wisp/internal/logger.gleam
··· 1 - import gleam/dict.{type Dict} 2 - import gleam/erlang/atom.{type Atom} 3 - import gleam/dynamic.{type Dynamic} 4 - 5 - pub type LogLevel { 6 - Emergency 7 - Alert 8 - Critical 9 - Error 10 - Warning 11 - Notice 12 - Info 13 - Debug 14 - } 15 - 16 - type DoNotLeak 17 - 18 - pub fn configure_logger() -> Nil { 19 - update_primary_config( 20 - dict.from_list([#(atom.create_from_string("level"), dynamic.from(Info))]), 21 - ) 22 - Nil 23 - } 24 - 25 - @external(erlang, "logger", "update_primary_config") 26 - fn update_primary_config(config: Dict(Atom, Dynamic)) -> DoNotLeak 27 - 28 - pub fn log(level: LogLevel, message: String) -> Nil { 29 - erlang_log(level, message) 30 - Nil 31 - } 32 - 33 - @external(erlang, "logger", "log") 34 - fn erlang_log(level: LogLevel, message: String) -> DoNotLeak
+1
test/wisp_test.gleam
··· 18 18 import wisp/testing 19 19 20 20 pub fn main() { 21 + wisp.configure_logger() 21 22 gleeunit.main() 22 23 } 23 24