@@ -62,6 +62,20 @@ defmodule Timber.Transports.IODevice do
62
62
63
63
_Defaults to `true`._
64
64
65
+ #### `escape_new_lines`
66
+
67
+ When `true`, new lines characters are escaped as `\\ n`.
68
+
69
+ When `false`, new lines characters are left alone.
70
+
71
+ This circumvents issues with output devices (like Heroku Logplex) that will tranform
72
+ line breaks into multiple log lines.
73
+
74
+ When the IODevice transport is initialized, it will check for the environment
75
+ variable `HEROKU`. If the environment variable is present, this will be set to
76
+ `true`. Otherwise, this defaults to `false`. Setting the value in your application
77
+ configuration will always override the initialized setting..
78
+
65
79
#### `format`
66
80
67
81
Determines the output format to use. Even though the Timber service is designed
@@ -134,6 +148,7 @@ defmodule Timber.Transports.IODevice do
134
148
135
149
@ default_colorize true
136
150
@ default_max_buffer_size 100
151
+ @ default_escape_new_lines false
137
152
@ default_format :json
138
153
@ default_print_log_level false
139
154
@ default_print_metadata true
@@ -147,6 +162,7 @@ defmodule Timber.Transports.IODevice do
147
162
buffer_size: non_neg_integer ,
148
163
max_buffer_size: pos_integer ,
149
164
colorize: boolean ,
165
+ escape_new_lines: boolean ,
150
166
format: :json | :logfmt ,
151
167
print_log_level: boolean ,
152
168
print_metadata: boolean ,
@@ -159,6 +175,7 @@ defmodule Timber.Transports.IODevice do
159
175
output: nil ,
160
176
buffer_size: 0 ,
161
177
colorize: @ default_colorize ,
178
+ escape_new_lines: @ default_escape_new_lines ,
162
179
format: @ default_format ,
163
180
max_buffer_size: @ default_max_buffer_size ,
164
181
print_log_level: @ default_print_log_level ,
@@ -181,7 +198,14 @@ defmodule Timber.Transports.IODevice do
181
198
# configuration
182
199
@ spec get_init_config ( ) :: Keyword . t
183
200
defp get_init_config ( ) do
184
- Application . get_env ( :timber , :io_device , [ ] )
201
+ heroku_env = System . get_env ( "HEROKU" )
202
+ heroku? = ! is_nil ( heroku_env )
203
+
204
+ init_env = [ escape_new_lines: heroku? ]
205
+
206
+ env = Application . get_env ( :timber , :io_device , [ ] )
207
+
208
+ Keyword . merge ( init_env , env )
185
209
end
186
210
187
211
@ spec get_device ( String . t | :no_file ) :: { :ok , IO . device } | { :error , Exception . t }
@@ -214,6 +238,7 @@ defmodule Timber.Transports.IODevice do
214
238
@ spec configure ( Keyword . t , t ) :: { :ok , t }
215
239
def configure ( options , state ) do
216
240
colorize = Keyword . get ( options , :colorize , @ default_colorize )
241
+ escape_new_lines = Keyword . get ( options , :escape_new_lines , @ default_escape_new_lines )
217
242
format = Keyword . get ( options , :format , @ default_format )
218
243
max_buffer_size = Keyword . get ( options , :max_buffer_size , @ default_max_buffer_size )
219
244
print_log_level = Keyword . get ( options , :print_log_level , @ default_print_log_level )
@@ -222,6 +247,7 @@ defmodule Timber.Transports.IODevice do
222
247
223
248
new_state = % { state |
224
249
colorize: colorize ,
250
+ escape_new_lines: escape_new_lines ,
225
251
format: format ,
226
252
max_buffer_size: max_buffer_size ,
227
253
print_log_level: print_log_level ,
@@ -251,10 +277,14 @@ defmodule Timber.Transports.IODevice do
251
277
[ ]
252
278
end
253
279
254
- output =
255
- [ message , metadata , " \n " ]
280
+ line_output =
281
+ [ message , metadata ]
256
282
|> add_log_level ( level_b , state . print_log_level )
257
283
|> add_timestamp ( timestamp , state . print_timestamps )
284
+ |> escape_new_lines ( state . escape_new_lines )
285
+
286
+ # Prevents the final new line from being escaped
287
+ output = [ line_output , ?\n ]
258
288
259
289
cond do
260
290
is_nil ( ref ) ->
@@ -308,6 +338,13 @@ defmodule Timber.Transports.IODevice do
308
338
defp log_level_color ( :error ) , do: :red
309
339
defp log_level_color ( _ ) , do: :normal
310
340
341
+ @ spec escape_new_lines ( IO . chardata , boolean ) :: IO . chardata
342
+ defp escape_new_lines ( msg , false ) , do: msg
343
+ defp escape_new_lines ( msg , true ) do
344
+ to_string ( msg )
345
+ |> String . replace ( << ?\n :: utf8 >> , << ?\\ :: utf8 , ?n :: utf8 >> )
346
+ end
347
+
311
348
@ spec write_buffer ( IO . chardata , t ) :: t
312
349
defp write_buffer ( output , state ) do
313
350
buffer = state . buffer
0 commit comments