@@ -36,59 +36,86 @@ defmodule RealtimeWeb.RealtimeChannel.Logging do
3636 end
3737
3838 @ doc """
39- Logs an error if the log level is set to error
39+ Logs an error if the log level is set to error.
40+
41+ Accepts an optional `throttle: {max_count, window_ms}` option to limit
42+ how many times the log is emitted per tenant+code within the given time window.
4043 """
41- @ spec maybe_log_error ( socket :: Phoenix.Socket . t ( ) , code :: binary ( ) , msg :: any ( ) ) :: { :error , % { reason: binary } }
42- def maybe_log_error ( socket , code , msg ) , do: maybe_log ( socket , :error , code , msg )
44+ @ spec maybe_log_error ( socket :: Phoenix.Socket . t ( ) , code :: binary ( ) , msg :: any ( ) , opts :: keyword ( ) ) ::
45+ { :error , % { reason: binary } }
46+ def maybe_log_error ( socket , code , msg , opts \\ [ ] ) , do: maybe_log ( socket , :error , code , msg , opts )
4347
4448 @ doc """
45- Logs a warning if the log level is set to warning
49+ Logs a warning if the log level is set to warning.
50+
51+ Accepts an optional `throttle: {max_count, window_ms}` option to limit
52+ how many times the log is emitted per tenant+code within the given time window.
4653 """
47- @ spec maybe_log_warning ( socket :: Phoenix.Socket . t ( ) , code :: binary ( ) , msg :: any ( ) ) :: { :error , % { reason: binary } }
48- def maybe_log_warning ( socket , code , msg ) , do: maybe_log ( socket , :warning , code , msg )
54+ @ spec maybe_log_warning ( socket :: Phoenix.Socket . t ( ) , code :: binary ( ) , msg :: any ( ) , opts :: keyword ( ) ) ::
55+ { :error , % { reason: binary } }
56+ def maybe_log_warning ( socket , code , msg , opts \\ [ ] ) , do: maybe_log ( socket , :warning , code , msg , opts )
4957
5058 @ doc """
51- Logs an info if the log level is set to info
59+ Logs an info if the log level is set to info.
5260 """
5361 @ spec maybe_log_info ( socket :: Phoenix.Socket . t ( ) , msg :: any ( ) ) :: :ok
54- def maybe_log_info ( socket , msg ) , do: maybe_log ( socket , :info , nil , msg )
62+ def maybe_log_info ( socket , msg ) , do: maybe_log ( socket , :info , nil , msg , [ ] )
5563
56- defp build_msg ( code , msg ) do
57- msg = stringify! ( msg )
58- if code , do: "#{ code } : #{ msg } " , else: msg
59- end
64+ defp build_msg ( nil , msg ) , do: stringify! ( msg )
65+ defp build_msg ( code , msg ) , do: "#{ code } : #{ stringify! ( msg ) } "
6066
61- defp log ( % { assigns: % { tenant: tenant , access_token: access_token } } , level , code , msg ) do
67+ defp log ( % { assigns: assigns } , level , code , msg ) do
68+ tenant = assigns . tenant
6269 Logger . metadata ( external_id: tenant , project: tenant )
63- if level in [ :error , :warning ] , do: update_metadata_with_token_claims ( access_token )
70+ enrich_metadata ( level , Map . get ( assigns , : access_token) )
6471 Logger . log ( level , msg , error_code: code )
65- if level in [ :error ] , do: emit_system_error ( level , code , tenant )
72+ emit_telemetry ( level , code , tenant )
6673 end
6774
68- defp maybe_log ( % { assigns: % { log_level: log_level } } = socket , level , code , msg ) do
69- msg = build_msg ( code , msg )
70- if Logger . compare_levels ( log_level , level ) != :gt , do: log ( socket , level , code , msg )
71- if level in [ :error , :warning ] , do: { :error , % { reason: msg } } , else: :ok
75+ defp enrich_metadata ( level , token ) when level in [ :error , :warning ] ,
76+ do: update_metadata_with_token_claims ( token )
77+
78+ defp enrich_metadata ( _level , _token ) , do: :ok
79+
80+ defp emit_telemetry ( :error , code , tenant ) ,
81+ do: Telemetry . execute ( [ :realtime , :channel , :error ] , % { count: 1 } , % { code: code , tenant: tenant } )
82+
83+ defp emit_telemetry ( _level , _code , _tenant ) , do: :ok
84+
85+ defp maybe_log ( % { assigns: % { log_level: log_level } } = socket , level , code , msg , opts ) do
86+ built_msg = build_msg ( code , msg )
87+ if Logger . compare_levels ( log_level , level ) != :gt , do: do_log ( socket , level , code , built_msg , opts )
88+ if level in [ :error , :warning ] , do: { :error , % { reason: built_msg } } , else: :ok
7289 end
7390
74- defp emit_system_error ( level , code , tenant_id ) ,
75- do: Telemetry . execute ( [ :realtime , :channel , level ] , % { count: 1 } , % { code: code , tenant: tenant_id } )
91+ defp do_log ( socket , level , code , msg , [ ] ) , do: log ( socket , level , code , msg )
92+
93+ defp do_log ( % { assigns: % { tenant: tenant } } = socket , level , code , msg , throttle: { max_count , window_ms } ) do
94+ key = { tenant , level , code }
95+
96+ case Cachex . get ( Realtime.LogThrottle , key ) do
97+ { :ok , nil } ->
98+ Cachex . put ( Realtime.LogThrottle , key , 1 , expire: window_ms )
99+ log ( socket , level , code , msg )
100+
101+ { :ok , count } when count < max_count ->
102+ Cachex . incr ( Realtime.LogThrottle , key )
103+ log ( socket , level , code , msg )
104+
105+ _ ->
106+ emit_telemetry ( level , code , tenant )
107+ end
108+ end
76109
77110 defp stringify! ( msg ) when is_binary ( msg ) , do: msg
78111 defp stringify! ( msg ) , do: inspect ( msg , pretty: true )
79112
80- defp update_metadata_with_token_claims ( nil ) , do: nil
113+ defp update_metadata_with_token_claims ( nil ) , do: :ok
81114
82115 defp update_metadata_with_token_claims ( token ) do
83116 case Joken . peek_claims ( token ) do
84- { :ok , claims } ->
85- sub = Map . get ( claims , "sub" )
86- exp = Map . get ( claims , "exp" )
87- iss = Map . get ( claims , "iss" )
88- Logger . metadata ( sub: sub , exp: exp , iss: iss )
89-
90- _ ->
91- nil
117+ { :ok , claims } -> Logger . metadata ( sub: claims [ "sub" ] , exp: claims [ "exp" ] , iss: claims [ "iss" ] )
118+ _ -> :ok
92119 end
93120 end
94121end
0 commit comments