From dda8cc8615d0a35f5608aab2faf508784e427ab4 Mon Sep 17 00:00:00 2001 From: vshakitskiy Date: Sat, 14 Mar 2026 18:49:50 +0300 Subject: [PATCH] remove string.inspect completely --- CHANGELOG.md | 4 +++ README.md | 9 ++--- dev/preview.gleam | 17 +++------ examples/manifest.toml | 7 ++-- examples/src/streaming_body.gleam | 10 ++---- examples/src/websocket.gleam | 26 ++++++-------- src/ewe/internal/http1/handler.gleam | 5 ++- src/ewe/internal/stream/chunked.gleam | 34 ++++++++---------- src/ewe/internal/stream/websocket.gleam | 46 ++++++++++++------------- 9 files changed, 69 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afb29e..e84f342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +# Unreleased + +- Remove all usage of `string.inspect` as it's a huge anti-pattern for logging. + # v3.0.4 - 13.03.2026 - Fix glisten being incorrectly supervised on start. diff --git a/README.md b/README.md index ab8cc1a..c9ad9b8 100644 --- a/README.md +++ b/README.md @@ -168,10 +168,7 @@ fn stream_resource( case consumer(chunk_size) { Ok(ewe.Consumed(data, next)) -> { logging.log(logging.Info, { - "Consumed " - <> int.to_string(bit_array.byte_size(data)) - <> " bytes: " - <> string.inspect(data) + "Consumed " <> int.to_string(bit_array.byte_size(data)) <> " bytes." }) process.send(subject, Chunk(data)) @@ -215,8 +212,8 @@ fn handle_stream(req: Request, chunk_size: Int) -> Response { Error(_) -> ewe.chunked_stop_abnormal("Failed to send chunk") } Done -> ewe.chunked_stop() - BodyError(body_error) -> - ewe.chunked_stop_abnormal(string.inspect(body_error)) + BodyError(_body_error) -> + ewe.chunked_stop_abnormal("failed to read body") } }, on_close: fn(_conn, _state) { diff --git a/dev/preview.gleam b/dev/preview.gleam index 4908da4..efb4a03 100644 --- a/dev/preview.gleam +++ b/dev/preview.gleam @@ -12,7 +12,6 @@ import gleam/otp/actor import gleam/otp/static_supervisor as supervisor import gleam/otp/supervision.{type ChildSpecification} import gleam/result -import gleam/string import logging import ewe.{type Request, type Response} @@ -99,13 +98,10 @@ fn pubsub_worker( logging.Info, "Publishing text message `" <> text <> "` to topic " <> topic, ) - Bytes(binary) -> + Bytes(_binary) -> logging.log( logging.Info, - "Publishing binary message `" - <> string.inspect(binary) - <> "` to topic " - <> topic, + "Publishing binary message to topic " <> topic, ) } @@ -284,10 +280,7 @@ fn stream_resource( case consumer(chunk_size) { Ok(ewe.Consumed(data, next)) -> { logging.log(logging.Info, { - "Consumed " - <> int.to_string(bit_array.byte_size(data)) - <> " bytes: " - <> string.inspect(data) + "Consumed " <> int.to_string(bit_array.byte_size(data)) <> " bytes" }) process.send(subject, Chunk(data)) @@ -325,8 +318,8 @@ fn handle_stream(req: Request, chunk_size: Int) -> Response { Error(_) -> ewe.chunked_stop_abnormal("Failed to send chunk") } Done -> ewe.chunked_stop() - BodyError(body_error) -> - ewe.chunked_stop_abnormal(string.inspect(body_error)) + BodyError(_body_error) -> + ewe.chunked_stop_abnormal("failed to read body") } }, on_close: fn(_conn, _state) { diff --git a/examples/manifest.toml b/examples/manifest.toml index 22f0329..d79294e 100644 --- a/examples/manifest.toml +++ b/examples/manifest.toml @@ -3,18 +3,17 @@ packages = [ { name = "compresso", version = "0.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_stdlib", "gleam_yielder", "logging"], otp_app = "compresso", source = "hex", outer_checksum = "8BE29A1EDA42F70826ED148EAE40C46BB3FC18E78FE472663DB01DD4A38172D4" }, - { name = "ewe", version = "3.0.1", build_tools = ["gleam"], requirements = ["compresso", "exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "logging", "websocks"], source = "local", path = ".." }, + { name = "ewe", version = "3.0.4", build_tools = ["gleam"], requirements = ["compresso", "exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "logging", "websocks"], source = "local", path = ".." }, { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, - { name = "gleam_stdlib", version = "0.69.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AAB0962BEBFAA67A2FBEE9EEE218B057756808DC9AF77430F5182C6115B3A315" }, + { name = "gleam_stdlib", version = "0.70.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86949BF5D1F0E4AC0AB5B06F235D8A5CC11A2DFC33BF22F752156ED61CA7D0FF" }, { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" }, { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, - { name = "glisten", version = "8.0.3", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "86B838196592D9EBDE7A1D2369AE3A51E568F7DD2D168706C463C42D17B95312" }, + { name = "glisten", version = "9.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging"], otp_app = "glisten", source = "hex", outer_checksum = "D92808C66F7D3F22F2289CD04CBA8151757AAE9CB3D86992F0C6DE32A41205E1" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, - { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, { name = "websocks", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_stdlib"], otp_app = "websocks", source = "hex", outer_checksum = "A13BF89A8AC63C478C0E9FE502C81A6CBB0513F427FA5C1DFD96383BF46D501D" }, ] diff --git a/examples/src/streaming_body.gleam b/examples/src/streaming_body.gleam index ae302c6..0b8c9c0 100644 --- a/examples/src/streaming_body.gleam +++ b/examples/src/streaming_body.gleam @@ -5,7 +5,6 @@ import gleam/http/request import gleam/http/response import gleam/int import gleam/result -import gleam/string import logging pub fn main() { @@ -76,8 +75,8 @@ fn handle_stream(req: Request, chunk_size: Int) -> Response { Error(_) -> ewe.chunked_stop_abnormal("Failed to send chunk") } Done -> ewe.chunked_stop() - BodyError(body_error) -> - ewe.chunked_stop_abnormal(string.inspect(body_error)) + BodyError(_body_error) -> + ewe.chunked_stop_abnormal("failed to read body") } }, on_close: fn(_conn, _state) { @@ -109,10 +108,7 @@ fn stream_resource( case consumer(chunk_size) { Ok(ewe.Consumed(data, next)) -> { logging.log(logging.Info, { - "Consumed " - <> int.to_string(bit_array.byte_size(data)) - <> " bytes: " - <> string.inspect(data) + "Consumed " <> int.to_string(bit_array.byte_size(data)) <> " bytes." }) process.send(subject, Chunk(data)) diff --git a/examples/src/websocket.gleam b/examples/src/websocket.gleam index 7db55bf..ac5de07 100644 --- a/examples/src/websocket.gleam +++ b/examples/src/websocket.gleam @@ -9,7 +9,6 @@ import gleam/option.{None, Some} import gleam/otp/actor import gleam/otp/static_supervisor as supervisor import gleam/otp/supervision.{type ChildSpecification} -import gleam/string import logging pub fn main() { @@ -19,12 +18,12 @@ pub fn main() { // Create a named pubsub process for topic-based message broadcasting. // Multiple clients can subscribe to different topics and receive messages // sent to those topics. - // + // let pubsub_name = process.new_name("pubsub") let pubsub = process.named_subject(pubsub_name) // Set up supervision for both pubsub and the web server. - // + // let assert Ok(_) = supervisor.new(supervisor.OneForAll) |> supervisor.add(pubsub_worker(pubsub_name)) @@ -67,12 +66,12 @@ type Broadcast { fn handle_topic(req: Request, pubsub: Subject(PubSubMessage), topic: String) { // Upgrade the HTTP connection to WebSocket. Unlike SSE, WebSocket is // bidirectional - both client and server can send messages at any time. - // + // ewe.upgrade_websocket( req, // Initialize the WebSocket connection. The selector allows receiving // messages from both the WebSocket and the pubsub system. - // + // on_init: fn(_conn, selector) { logging.log( logging.Info, @@ -84,7 +83,7 @@ fn handle_topic(req: Request, pubsub: Subject(PubSubMessage), topic: String) { let state = WebsocketState(pubsub:, topic:, client:) // Add the client subject to the selector to receive broadcast messages. - // + // let selector = process.select(selector, client) #(state, selector) @@ -104,7 +103,7 @@ fn handle_topic(req: Request, pubsub: Subject(PubSubMessage), topic: String) { // Handle three types of messages: text from client, binary from client, // and broadcast messages from the pubsub system. -// +// fn handle_websocket_message( conn: ewe.WebsocketConnection, state: WebsocketState, @@ -112,21 +111,21 @@ fn handle_websocket_message( ) -> ewe.WebsocketNext(WebsocketState, Broadcast) { case msg { // Text message from the client - broadcast to all subscribers. - // + // ewe.Text(text) -> { process.send(state.pubsub, Publish(state.topic, Text(text))) ewe.websocket_continue(state) } // Binary message from the client - broadcast to all subscribers. - // + // ewe.Binary(binary) -> { process.send(state.pubsub, Publish(state.topic, Bytes(binary))) ewe.websocket_continue(state) } // User message from the pubsub - forward to this client. - // + // ewe.User(message) -> { let assert Ok(_) = case message { Text(text) -> ewe.send_text_frame(conn, text) @@ -191,13 +190,10 @@ fn handle_pubsub_message(state, message) { logging.Info, "Publishing text message `" <> text <> "` to topic " <> topic, ) - Bytes(binary) -> + Bytes(_binary) -> logging.log( logging.Info, - "Publishing binary message `" - <> string.inspect(binary) - <> "` to topic " - <> topic, + "Publishing binary message to topic " <> topic, ) } diff --git a/src/ewe/internal/http1/handler.gleam b/src/ewe/internal/http1/handler.gleam index 4f48cd1..9658625 100644 --- a/src/ewe/internal/http1/handler.gleam +++ b/src/ewe/internal/http1/handler.gleam @@ -125,9 +125,8 @@ fn call( ) -> Result(Http1Handler, Nil) { let response = case exception.rescue(fn() { handler(request) }) { Ok(response) -> response - Error(e) -> { - logging.log(logging.Error, string.inspect(e)) - + Error(_exception) -> { + logging.log(logging.Error, "Caught crash in request handler") response.set_header(on_crash, "connection", "close") } } diff --git a/src/ewe/internal/stream/chunked.gleam b/src/ewe/internal/stream/chunked.gleam index 09ed9b0..c5ab1f5 100644 --- a/src/ewe/internal/stream/chunked.gleam +++ b/src/ewe/internal/stream/chunked.gleam @@ -5,14 +5,13 @@ import gleam/erlang/process.{type Subject} import gleam/http/response.{type Response} import gleam/otp/actor import gleam/result -import gleam/string import glisten import glisten/socket.{type Socket} import glisten/transport.{type Transport} import logging /// Sends a response for a chunked transfer encoding. -/// +/// pub fn send_response( resp: Response(a), transport: Transport, @@ -28,13 +27,13 @@ pub fn send_response( } /// Represents a chunked response connection. -/// +/// pub type ChunkedBody { ChunkedBody(transport: Transport, socket: Socket) } /// Represents an instruction on how chunked response should proceed. -/// +/// pub type ChunkedNext(user_state) { Continue(user_state) NormalStop @@ -42,7 +41,7 @@ pub type ChunkedNext(user_state) { } /// Starts a new chunked response connection. -/// +/// pub fn start( transport: Transport, socket: Socket, @@ -75,16 +74,13 @@ pub fn start( actor.stop() } Error(socket_reason) -> { - logging.log( - logging.Error, - "Failed to send end of chunked response: " - <> string.inspect(socket_reason), - ) + let message = + "Socket error occured while trying to send the end of chunked response: " + <> socket.reason_to_string(socket_reason) + + logging.log(logging.Error, message) on_close(conn, state) - actor.stop_abnormal( - "Failed to send end of chunked response: " - <> string.inspect(socket_reason), - ) + actor.stop_abnormal(message) } } } @@ -103,7 +99,7 @@ pub fn start( } /// Maps actor's starting value to Nil. -/// +/// fn after_start( started: actor.Started(Subject(user_message)), transport: Transport, @@ -116,7 +112,7 @@ fn after_start( } /// Sends the end marker for chunked transfer encoding. -/// +/// fn send_end( transport: Transport, socket: Socket, @@ -125,7 +121,7 @@ fn send_end( } /// Sends a chunk to the client. -/// +/// pub fn send_chunk( transport: Transport, socket: Socket, @@ -140,12 +136,12 @@ pub fn send_chunk( } /// Converts an integer to a hexadecimal string. -/// +/// fn to_hex_string(integer: Int) -> String { integer_to_list(integer, 16) } /// Converts an integer to a string in the given base. -/// +/// @external(erlang, "erlang", "integer_to_list") fn integer_to_list(integer: Int, base: Int) -> String diff --git a/src/ewe/internal/stream/websocket.gleam b/src/ewe/internal/stream/websocket.gleam index c729927..736494c 100644 --- a/src/ewe/internal/stream/websocket.gleam +++ b/src/ewe/internal/stream/websocket.gleam @@ -7,7 +7,6 @@ import gleam/erlang/process.{type Selector, type Subject} import gleam/option.{type Option, None, Some} import gleam/otp/actor import gleam/result -import gleam/string import glisten/socket.{type Socket, type SocketReason} import glisten/socket/options.{ActiveMode, Count} import glisten/transport.{type Transport} @@ -15,7 +14,7 @@ import logging import websocks /// Represents a WebSocket connection. -/// +/// pub type WebsocketConnection { WebsocketConnection( transport: Transport, @@ -25,14 +24,14 @@ pub type WebsocketConnection { } /// Messages that can be sent to or received from the WebSocket. -/// +/// pub type WebsocketMessage(user_message) { Frame(websocks.Frame) UserMessage(user_message) } /// Control flow for WebSocket message handling. -/// +/// pub type WebsocketNext(user_state, user_message) { Continue(user_state: user_state, selector: Option(Selector(user_message))) NormalStop @@ -40,13 +39,13 @@ pub type WebsocketNext(user_state, user_message) { } // Internal state maintained by the WebSocket actor. -// +// type WebsocketState(user_state) { WebsocketState(user_state: user_state, context: websocks.Context) } // Type alias for actor next steps. -// +// type ActorNext(user_state, user_message) = actor.Next(WebsocketState(user_state), InternalMessage(user_message)) @@ -98,7 +97,7 @@ const non_owning_process = "Sending WebSocket message from non-owning process" const socket_active_count = 100 /// Starts a new WebSocket connection. -/// +/// pub fn start( transport: Transport, socket: Socket, @@ -165,7 +164,7 @@ pub fn start( } // Creates selector for glisten socket events. -// +// fn create_socket_selector() -> Selector(InternalMessage(user_message)) { process.new_selector() |> process.select_record(atom.create("tcp"), 2, fn(record) { @@ -183,7 +182,7 @@ fn create_socket_selector() -> Selector(InternalMessage(user_message)) { fn coerce_tcp_message(record: dynamic.Dynamic) -> BitArray // Handles incoming packet data, decoding frames and processing them. -// +// fn handle_valid_packet( transport: Transport, socket: Socket, @@ -227,7 +226,7 @@ fn handle_valid_packet( } // Represents the state of the WebSocket connection when resolving frames. -// +// type ResolveState(user_state, user_message) { ResolveState( socket: Socket, @@ -238,7 +237,7 @@ type ResolveState(user_state, user_message) { } /// Processes a list of frames sequentially. -/// +/// fn handle_frame( state: ResolveState(user_state, user_message), context: websocks.Context, @@ -318,7 +317,7 @@ fn handle_frame( } // Handles user messages sent to the WebSocket. -// +// fn handle_user_message( transport: Transport, socket: Socket, @@ -355,7 +354,7 @@ fn handle_user_message( } // Handles WebSocket connection closure. -// +// fn handle_close( on_close: OnClose(user_state), state: WebsocketState(user_state), @@ -378,7 +377,7 @@ fn handle_close( } // Maps actor's starting value to Nil. -// +// fn after_start( started: actor.Started(Subject(InternalMessage(user_message))), transport: Transport, @@ -393,7 +392,7 @@ fn after_start( } /// Sends a frame to the WebSocket. -/// +/// pub fn send_frame( encoder: fn(BitArray, websocks.Context, Option(BitArray)) -> BitArray, transport: Transport, @@ -410,11 +409,10 @@ pub fn send_frame( case frame { Ok(frame) -> frame - Error(reason) -> { + Error(_socket_reason) -> { logging.log( logging.Error, - "Frame should be sent from the WebSocket connection, but was sent from different process: " - <> string.inspect(reason), + "Frame should be sent from the WebSocket connection, but was sent from different process.", ) panic as non_owning_process } @@ -422,7 +420,7 @@ pub fn send_frame( } /// Sends a close frame to the WebSocket. -/// +/// pub fn send_close_frame( transport: Transport, socket: Socket, @@ -438,12 +436,14 @@ pub fn send_close_frame( case frame { Ok(Ok(Nil)) -> NormalStop Ok(Error(reason)) -> - AbnormalStop("Failed to send close frame: " <> string.inspect(reason)) - Error(reason) -> { + AbnormalStop( + "Socket error occured while trying to send close frame: " + <> socket.reason_to_string(reason), + ) + Error(_reason) -> { logging.log( logging.Error, - "Frame should be sent from the WebSocket connection, but was sent from different process: " - <> string.inspect(reason), + "Frame should be sent from the WebSocket connection, but was sent from different process.", ) panic as non_owning_process -- 2.51.2