1+ using System ;
2+ using System . Collections . Generic ;
13using Azure . DataApiBuilder . Config . ObjectModel ;
4+ using Azure . DataApiBuilder . Core . Telemetry ;
25using Microsoft . Extensions . Logging ;
36
47namespace Azure . DataApiBuilder . Service . Telemetry
58{
6- public class DynamicLogLevelProvider
9+ /// <summary>
10+ /// Provides dynamic log level control with support for CLI override, runtime config, and MCP.
11+ /// </summary>
12+ public class DynamicLogLevelProvider : ILogLevelController
713 {
14+ /// <summary>
15+ /// Maps MCP log level strings to Microsoft.Extensions.Logging.LogLevel.
16+ /// MCP levels: debug, info, notice, warning, error, critical, alert, emergency.
17+ /// </summary>
18+ private static readonly Dictionary < string , LogLevel > _mcpLevelMapping = new ( StringComparer . OrdinalIgnoreCase )
19+ {
20+ [ "debug" ] = LogLevel . Debug ,
21+ [ "info" ] = LogLevel . Information ,
22+ [ "notice" ] = LogLevel . Information , // MCP "notice" maps to Information (no direct equivalent)
23+ [ "warning" ] = LogLevel . Warning ,
24+ [ "error" ] = LogLevel . Error ,
25+ [ "critical" ] = LogLevel . Critical ,
26+ [ "alert" ] = LogLevel . Critical , // MCP "alert" maps to Critical
27+ [ "emergency" ] = LogLevel . Critical // MCP "emergency" maps to Critical
28+ } ;
29+
830 public LogLevel CurrentLogLevel { get ; private set ; }
31+
932 public bool IsCliOverridden { get ; private set ; }
1033
11- public void SetInitialLogLevel ( LogLevel logLevel = LogLevel . Error , bool isCliOverridden = false )
34+ public bool IsConfigOverridden { get ; private set ; }
35+
36+ public void SetInitialLogLevel ( LogLevel logLevel = LogLevel . Error , bool isCliOverridden = false , bool isConfigOverridden = false )
1237 {
1338 CurrentLogLevel = logLevel ;
1439 IsCliOverridden = isCliOverridden ;
40+ IsConfigOverridden = isConfigOverridden ;
1541 }
1642
1743 public void UpdateFromRuntimeConfig ( RuntimeConfig runtimeConfig )
@@ -20,7 +46,52 @@ public void UpdateFromRuntimeConfig(RuntimeConfig runtimeConfig)
2046 if ( ! IsCliOverridden )
2147 {
2248 CurrentLogLevel = runtimeConfig . GetConfiguredLogLevel ( ) ;
49+
50+ // Track if config explicitly set a log level (not just using defaults)
51+ IsConfigOverridden = ! runtimeConfig . IsLogLevelNull ( ) ;
52+ }
53+ }
54+
55+ /// <summary>
56+ /// Updates the log level from an MCP logging/setLevel request.
57+ /// Precedence (highest to lowest):
58+ /// 1. CLI --LogLevel flag (IsCliOverridden = true)
59+ /// 2. Config runtime.telemetry.log-level (IsConfigOverridden = true)
60+ /// 3. MCP logging/setLevel
61+ ///
62+ /// If CLI or Config overrode, this method accepts the request silently but does not change the level.
63+ /// </summary>
64+ /// <param name="mcpLevel">The MCP log level string (e.g., "debug", "info", "warning", "error").</param>
65+ /// <returns>True if the level was changed; false if CLI/Config override prevented the change or level was invalid.</returns>
66+ public bool UpdateFromMcp ( string mcpLevel )
67+ {
68+ // If CLI overrode the log level, accept the request but don't change anything.
69+ // This prevents MCP clients from getting errors, but CLI wins.
70+ if ( IsCliOverridden )
71+ {
72+ return false ;
2373 }
74+
75+ // If Config explicitly set the log level, accept the request but don't change anything.
76+ // Config has second precedence after CLI.
77+ if ( IsConfigOverridden )
78+ {
79+ return false ;
80+ }
81+
82+ if ( string . IsNullOrWhiteSpace ( mcpLevel ) )
83+ {
84+ return false ;
85+ }
86+
87+ if ( _mcpLevelMapping . TryGetValue ( mcpLevel , out LogLevel logLevel ) )
88+ {
89+ CurrentLogLevel = logLevel ;
90+ return true ;
91+ }
92+
93+ // Unknown level - don't change, but don't fail either
94+ return false ;
2495 }
2596
2697 public bool ShouldLog ( LogLevel logLevel )
0 commit comments