diff --git a/CommandLineOptions.cs b/CommandLineOptions.cs index 9ddbdc2..b72d8c8 100644 --- a/CommandLineOptions.cs +++ b/CommandLineOptions.cs @@ -4,22 +4,22 @@ public class CommandLineOptions { [JsonProperty(PropertyName = "instances")] - public List Instances { get; set; } + public List Instances { get; set; } = new List(); [JsonProperty(PropertyName = "port")] public int Port { get; set; } [JsonProperty(PropertyName = "waitStats")] - public WaitStats WaitStats { get; set; } + public WaitStats WaitStats { get; set; } = new WaitStats(); [JsonProperty(PropertyName = "performanceCounters")] - public PerformanceCounters PerformanceCounters { get; set; } + public PerformanceCounters PerformanceCounters { get; set; } = new PerformanceCounters(); } public class PerformanceCounters { [JsonProperty(PropertyName = "templateFiles")] - public string[] TemplateFiles { get; set; } + public string[] TemplateFiles { get; set; } = new string[0]; } @@ -27,12 +27,12 @@ public class PerformanceCounters public class SQLServerInstance { [JsonProperty(PropertyName = "connectionString")] - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = string.Empty; } public class WaitStats { [JsonProperty(PropertyName = "templateFiles")] - public string[] TemplateFiles { get; set; } + public string[] TemplateFiles { get; set; } = new string[0]; } \ No newline at end of file diff --git a/Counters/PerformanceCounters.cs b/Counters/PerformanceCounters.cs index 63f2688..cdc155f 100644 --- a/Counters/PerformanceCounters.cs +++ b/Counters/PerformanceCounters.cs @@ -29,7 +29,7 @@ public override string ToString() public class PerformanceCounters { - private static HashSet EnabledCounters { get; set; } + private static HashSet? EnabledCounters { get; set; } = null; static Dictionary _dGraf; public SQLServerInfo SQLServerInfo; @@ -38,9 +38,10 @@ public class PerformanceCounters static PerformanceCounters() { _dGraf = new Dictionary(); - using (System.IO.StreamReader sr = new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("MindFlavor.SQLServerExporter.embed.PerformanceCountersMapping.csv"))) + + using (System.IO.StreamReader sr = new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("MindFlavor.SQLServerExporter.embed.PerformanceCountersMapping.csv")!)) { - string s; + string? s; while ((s = sr.ReadLine()) != null) { string[] toks = s.Split(','); @@ -71,7 +72,7 @@ public PerformanceCounters(HttpContext context, SQLServerInfo sqlServerInfo) { EnabledCounters = new HashSet(); - foreach (var file in Program.CommandLineOptions.PerformanceCounters.TemplateFiles) + foreach (var file in Program.CommandLineOptions!.PerformanceCounters.TemplateFiles) { System.IO.FileInfo fi = new System.IO.FileInfo(file); logger.LogInformation($"Loading performance counter template to include from {fi.FullName}..."); @@ -79,7 +80,7 @@ public PerformanceCounters(HttpContext context, SQLServerInfo sqlServerInfo) using (System.IO.StreamReader sr = new System.IO.StreamReader(new System.IO.FileStream(fi.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))) { - string str; + string? str; while ((str = sr.ReadLine()) != null) { if (str.StartsWith("#")) @@ -99,71 +100,76 @@ private static string KeyFromObjectNameAndCounterName(string objectName, string public async Task QueryAndSerializeData() { - using (SqlConnection conn = new SqlConnection(this.SQLServerInfo.ConnectionString)) + if (EnabledCounters == null) + throw new Exception("EnabledCounters must not be null at this phase."); + else { - logger.LogDebug($"About to open connection to {this.SQLServerInfo.Name}"); - await conn.OpenAsync(); + using (SqlConnection conn = new SqlConnection(this.SQLServerInfo.ConnectionString)) + { + logger.LogDebug($"About to open connection to {this.SQLServerInfo.Name}"); + await conn.OpenAsync(); - System.Text.StringBuilder sb = new System.Text.StringBuilder(); + System.Text.StringBuilder sb = new System.Text.StringBuilder(); - string tsql = TSQLStore.ProbeTSQL("performance_counters", this.SQLServerInfo); + string tsql = TSQLStore.ProbeTSQL("performance_counters", this.SQLServerInfo); - logger.LogDebug($"Probing performance counters for {this.SQLServerInfo.Name}, version {this.SQLServerInfo.Version} returned {tsql}"); + logger.LogDebug($"Probing performance counters for {this.SQLServerInfo.Name}, version {this.SQLServerInfo.Version} returned {tsql}"); - using (SqlCommand cmd = new SqlCommand(tsql, conn)) - { - using (var reader = await cmd.ExecuteReaderAsync()) + using (SqlCommand cmd = new SqlCommand(tsql, conn)) { - while (await reader.ReadAsync()) + using (var reader = await cmd.ExecuteReaderAsync()) { - string objectName = reader.GetString(0).Trim(); - // this will strip the prefix because is dependent on the instance - // name (like MSSQL$SQL17:Locks, MSSQL$SQL17:Databases etc...) - // and we will store the info in the instance attribute instead - int idx = objectName.IndexOf(":"); - if (idx != -1) - objectName = objectName.Substring(idx + 1); - - string counterName = reader.GetString(1).Trim(); - string instanceName = reader.IsDBNull(2) ? null : reader.GetString(2).Trim(); - long cntr_value = reader.GetInt64(3); - - string key = KeyFromObjectNameAndCounterName(objectName, counterName); - - GrafanaPerformanceCounter gpc; - if (_dGraf.TryGetValue(key, out gpc)) + while (await reader.ReadAsync()) { - // skip this is it's not in the enabled performance counters - // as specified by the template files configured. - if (!EnabledCounters.Contains(gpc.name)) + string objectName = reader.GetString(0).Trim(); + // this will strip the prefix because is dependent on the instance + // name (like MSSQL$SQL17:Locks, MSSQL$SQL17:Databases etc...) + // and we will store the info in the instance attribute instead + int idx = objectName.IndexOf(":"); + if (idx != -1) + objectName = objectName.Substring(idx + 1); + + string counterName = reader.GetString(1).Trim(); + string? instanceName = reader.IsDBNull(2) ? null : reader.GetString(2).Trim(); + long cntr_value = reader.GetInt64(3); + + string key = KeyFromObjectNameAndCounterName(objectName, counterName); + + GrafanaPerformanceCounter gpc; + if (_dGraf.TryGetValue(key, out gpc)) { - logger.LogDebug($"PerformanceCounter {gpc} will be skipped because it's not in any configured template file"); - continue; - } + // skip this is it's not in the enabled performance counters + // as specified by the template files configured. + if (!EnabledCounters.Contains(gpc.name)) + { + logger.LogDebug($"PerformanceCounter {gpc} will be skipped because it's not in any configured template file"); + continue; + } + + string gpcName = $"sql_pc_{gpc.name}"; - string gpcName = $"sql_pc_{gpc.name}"; + sb.Append($"# TYPE {gpcName} {gpc.type}\n"); - sb.Append($"# TYPE {gpcName} {gpc.type}\n"); + string completeName = $"{gpcName}{{instance=\"{this.SQLServerInfo.Name}\""; - string completeName = $"{gpcName}{{instance=\"{this.SQLServerInfo.Name}\""; + if (!string.IsNullOrEmpty(instanceName)) + { + completeName += $", counter_instance=\"{instanceName}\""; + } + completeName += "}"; - if (!string.IsNullOrEmpty(instanceName)) + sb.Append($"{completeName} {cntr_value.ToString()}\n"); + } + else { - completeName += $", counter_instance=\"{instanceName}\""; + logger.LogWarning($"entry {key} not mapped in the mapping file! Ignored in the output"); } - completeName += "}"; - - sb.Append($"{completeName} {cntr_value.ToString()}\n"); - } - else - { - logger.LogWarning($"entry {key} not mapped in the mapping file! Ignored in the output"); } } } - } - return sb.ToString(); + return sb.ToString(); + } } } } diff --git a/Counters/WaitStats.cs b/Counters/WaitStats.cs index 1faa579..acf677a 100644 --- a/Counters/WaitStats.cs +++ b/Counters/WaitStats.cs @@ -21,8 +21,8 @@ public class WaitStats public SQLServerInfo SQLServerInfo; private ILogger logger; - private static HashSet Waits { get; set; } - private static string TSQLQuery { get; set; } + private static HashSet? Waits { get; set; } + private static string TSQLQuery { get; set; } = string.Empty; public WaitStats(HttpContext context, SQLServerInfo sqlServerInfo) { @@ -38,7 +38,7 @@ public WaitStats(HttpContext context, SQLServerInfo sqlServerInfo) { Waits = new HashSet(); - foreach (var file in Program.CommandLineOptions.WaitStats.TemplateFiles) + foreach (var file in Program.CommandLineOptions!.WaitStats.TemplateFiles) { System.IO.FileInfo fi = new System.IO.FileInfo(file); logger.LogInformation($"Loading wait stats template to include from {fi.FullName}..."); @@ -46,7 +46,7 @@ public WaitStats(HttpContext context, SQLServerInfo sqlServerInfo) using (System.IO.StreamReader sr = new System.IO.StreamReader(new System.IO.FileStream(fi.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))) { - string str; + string? str; while ((str = sr.ReadLine()) != null) { if (str.StartsWith("#")) diff --git a/FileHandler.cs b/FileHandler.cs index c02baa1..53040b2 100644 --- a/FileHandler.cs +++ b/FileHandler.cs @@ -17,7 +17,7 @@ public static void Handler(IApplicationBuilder app) return; } - string s = null; + string s; using (FileStream fs = new FileInfo("C:\\tmp\\server-metrics-collectd_rev1.json").Open(FileMode.Open, FileAccess.Read, FileShare.Read)) { using (StreamReader sr = new StreamReader(fs)) diff --git a/MindFlavor.SQLServerExporter.csproj b/MindFlavor.SQLServerExporter.csproj index bbf7a99..d84f4b3 100644 --- a/MindFlavor.SQLServerExporter.csproj +++ b/MindFlavor.SQLServerExporter.csproj @@ -1,9 +1,9 @@ - netcoreapp2.2 + netcoreapp3.1 InProcess - 0.2.0.0 + 0.3.0.0 Francesco Cogno Apache License Version 2.0 https://raw.githubusercontent.com/MindFlavor/prometheus_sql_server_exporter/master/LICENSE @@ -11,10 +11,13 @@ https://github.com/MindFlavor/prometheus_sql_server_exporter + + enable + + - - - + + diff --git a/Program.cs b/Program.cs index dfc313a..26b1e4d 100644 --- a/Program.cs +++ b/Program.cs @@ -14,14 +14,14 @@ namespace MindFlavor.SQLServerExporter { public class Program { - public static CommandLineOptions CommandLineOptions { get; private set; } + public static CommandLineOptions? CommandLineOptions { get; private set; } public static void Main(string[] args) { var assemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; - System.Console.WriteLine($"Prometheus SQL Server Exporter v{assemblyVersion.ToString()}"); + System.Console.WriteLine($"Prometheus SQL Server Exporter v{assemblyVersion?.ToString() ?? "no version"}"); System.Console.WriteLine($"Licensed under Apache License 2.0\n"); - string configFile = null; + string? configFile = null; for (int i = 0; (i < args.Length - 1 && configFile == null); i++) if (args[i].ToLower() == "-c") configFile = args[i + 1]; @@ -32,7 +32,7 @@ public static void Main(string[] args) return; } - string jsonContents = null; + string? jsonContents = null; using (System.IO.StreamReader sr = new StreamReader(new System.IO.FileStream(configFile, FileMode.Open, FileAccess.Read, FileShare.Read))) { jsonContents = sr.ReadToEnd(); @@ -47,7 +47,7 @@ public static void Main(string[] args) public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseUrls($"http://*:{CommandLineOptions.Port}") + .UseUrls($"http://*:{CommandLineOptions!.Port}") .UseStartup(); } } diff --git a/SQLServerHandler.cs b/SQLServerHandler.cs index caa3aab..c59057e 100644 --- a/SQLServerHandler.cs +++ b/SQLServerHandler.cs @@ -31,7 +31,7 @@ public static void Handler(IApplicationBuilder app) List lTasks = new List(); ConcurrentBag bag = new ConcurrentBag(); - logger.LogDebug($"Before foreach(... {Program.CommandLineOptions.Instances.Count})"); + logger.LogDebug($"Before foreach(... {Program.CommandLineOptions!.Instances.Count})"); try { foreach (var instance in Program.CommandLineOptions.Instances) diff --git a/Startup.cs b/Startup.cs index e1fc4f9..11779e3 100644 --- a/Startup.cs +++ b/Startup.cs @@ -27,13 +27,13 @@ public Startup(IConfiguration configuration, ILogger logger) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) + if (env.EnvironmentName == "Development") { app.UseDeveloperExceptionPage(); } diff --git a/TSQLStore.cs b/TSQLStore.cs index c682e08..18f3fde 100644 --- a/TSQLStore.cs +++ b/TSQLStore.cs @@ -18,9 +18,15 @@ static TSQLStore() .Select(i => { // extract stream - using (System.IO.StreamReader sr = new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(i))) + System.IO.Stream? s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(i); + if (s == null) + throw new Exception($"Resource stream not found ({i})"); + else { - return new Tuple(i, sr.ReadToEnd()); + using (System.IO.StreamReader sr = new System.IO.StreamReader(s)) + { + return new Tuple(i, sr.ReadToEnd()); + } } }) .Select(tuple => new Tuple(tuple.Item1.Substring(PREFIX.Length + 1), tuple.Item2));