@@ -168,6 +168,81 @@ func Test_DetectLogLevels(t *testing.T) {
168168 })
169169 })
170170
171+ t .Run ("detected_level with mixed case value gets normalized to lowercase" , func (t * testing.T ) {
172+ // Test various mixed case values
173+ testCases := []struct {
174+ input string
175+ expected string
176+ }{
177+ {"WaRn" , constants .LogLevelWarn },
178+ {"InFo" , constants .LogLevelInfo },
179+ {"CRITICAL" , constants .LogLevelCritical },
180+ {"Debug" , constants .LogLevelDebug },
181+ {"FaTaL" , constants .LogLevelFatal },
182+ {"tRaCe" , constants .LogLevelTrace },
183+ {"ERROR" , constants .LogLevelError },
184+ }
185+
186+ for _ , tc := range testCases {
187+ // Create a fresh setup for each test case
188+ limits , ingester := setup (true )
189+ distributors , _ := prepare (t , 1 , 5 , limits , func (_ string ) (ring_client.PoolClient , error ) { return ingester , nil })
190+
191+ writeReq := makeWriteRequestWithLabels (1 , 10 , []string {`{foo="bar"}` }, false , false , false )
192+ writeReq .Streams [0 ].Entries [0 ].Line = `test log`
193+ writeReq .Streams [0 ].Entries [0 ].StructuredMetadata = push.LabelsAdapter {
194+ {
195+ Name : constants .LevelLabel ,
196+ Value : tc .input ,
197+ },
198+ }
199+
200+ _ , err := distributors [0 ].Push (ctx , writeReq )
201+ require .NoError (t , err )
202+ topVal := ingester .Peek ()
203+
204+ sm := topVal .Streams [0 ].Entries [0 ].StructuredMetadata
205+ require .Len (t , sm , 1 )
206+ require .Equal (t , constants .LevelLabel , sm [0 ].Name )
207+ require .Equal (t , tc .expected , sm [0 ].Value , "Input %q should normalize to %q" , tc .input , tc .expected )
208+ }
209+ })
210+
211+ t .Run ("level from stream labels gets normalized to lowercase in detected_level" , func (t * testing.T ) {
212+ // Test various mixed case values in stream labels
213+ testCases := []struct {
214+ streamLabel string
215+ expected string
216+ }{
217+ {`{foo="bar", level="ERROR"}` , constants .LogLevelError },
218+ {`{foo="bar", level="WaRn"}` , constants .LogLevelWarn },
219+ {`{foo="bar", level="InFo"}` , constants .LogLevelInfo },
220+ {`{foo="bar", level="CRITICAL"}` , constants .LogLevelCritical },
221+ {`{foo="bar", level="Debug"}` , constants .LogLevelDebug },
222+ {`{foo="bar", level="FaTaL"}` , constants .LogLevelFatal },
223+ {`{foo="bar", level="tRaCe"}` , constants .LogLevelTrace },
224+ }
225+
226+ for _ , tc := range testCases {
227+ // Create a fresh setup for each test case
228+ limits , ingester := setup (true )
229+ distributors , _ := prepare (t , 1 , 5 , limits , func (_ string ) (ring_client.PoolClient , error ) { return ingester , nil })
230+
231+ writeReq := makeWriteRequestWithLabels (1 , 10 , []string {tc .streamLabel }, false , false , false )
232+ writeReq .Streams [0 ].Entries [0 ].Line = `log message without level`
233+
234+ _ , err := distributors [0 ].Push (ctx , writeReq )
235+ require .NoError (t , err )
236+ topVal := ingester .Peek ()
237+
238+ // Verify that detected_level is normalized to lowercase
239+ sm := topVal .Streams [0 ].Entries [0 ].StructuredMetadata
240+ require .Len (t , sm , 1 , "Expected detected_level in structured metadata for stream label %s" , tc .streamLabel )
241+ require .Equal (t , constants .LevelLabel , sm [0 ].Name )
242+ require .Equal (t , tc .expected , sm [0 ].Value , "Stream label %q should normalize to %q in detected_level" , tc .streamLabel , tc .expected )
243+ }
244+ })
245+
171246 t .Run ("indexed OTEL severity takes precedence over structured metadata or log line" , func (t * testing.T ) {
172247 limits , ingester := setup (true )
173248 distributors , _ := prepare (t , 1 , 5 , limits , func (_ string ) (ring_client.PoolClient , error ) { return ingester , nil })
0 commit comments