Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
edchapel committed Aug 3, 2013
2 parents 1d8fef6 + b4bfe5f commit 61bed40
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 104 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A plugin for monitoring Microsoft SQL Server using the New Relic platform.

## Installation instructions

1. [Download the files](https://s3.amazonaws.com/new_relic_platform_plugin_binary_hosting/ms_sql_plugin/NewRelic.Microsoft.SqlServer.Plugin.zip)
1. [Download the files](https://rpm.newrelic.com/plugins/55/b100e5e011d544ba024e265887b4dff3) from New Relic.
2. Unpack them to something like `C:\Program Files\New Relic\MicrosoftSQLServerPlugin\` (we'll call this `INSTALLDIR`.) on a server that has access to the SQL server(s) you want to monitor. In general, that means the agent could run on the server hosting the SQL server or another locally connected machine which network access to the SQL server.
3. Configure the plugin.
1. Run a text editor **as administrator** and open the file `INSTALLDIR\NewRelic.Microsoft.SqlServer.Plugin.exe.config`.
Expand All @@ -25,14 +25,16 @@ A plugin for monitoring Microsoft SQL Server using the New Relic platform.
`connectionString="Server=tcp:zzz.database.windows.net,1433;Database=CustomerDB;User ID=NewRelic@zzz;`
`Password=foobar;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`
4. Verify the settings.
1. Open a command prompt running **as administrator** to `INSTALLDIR`.
2. Run the plugin in read-only mode: `NewRelic.Microsoft.SqlServer.Plugin.exe --collect-only`
1. Open a command prompt, **not** running as administrator, to `INSTALLDIR`.
2. Run the plugin in read-only, test mode: `NewRelic.Microsoft.SqlServer.Plugin.exe --test`
3. If there are no errors, move on to installing the service.
5. Install the plugin as a Windows service.
1. Use the command prompt from step #4.1 or open it again.
1. Open a new command prompt, **running as administrator**, to `INSTALLDIR`.
2. Execute: `NewRelic.Microsoft.SqlServer.Plugin.exe --install` and ensure you see the message
`Service NewRelicSQLServerPlugin has been successfully installed.`
3. Start the service: net start NewRelicSQLServerPlugin
3. Start the service: `net start NewRelicSQLServerPlugin`
4. Review the log file at `C:\ProgramData\New Relic\MicrosoftSQLServerPlugin\SqlMonitor.log` to confirm the service is running. Look at the end of the file for a successful startup similar to the output of step 4.2., but with `[Main] INFO - Windows Service: Yes`, indicating the Windows service is running.
5. After about 5 minutes, data is available in your New Relic dashboard in the 'MS SQL' and/or 'Azure SQL' area.

## Configure permissions

Expand Down Expand Up @@ -126,6 +128,7 @@ SQL Server
LEFT JOIN sys.sysprocesses s ON s.dbid = d.database_id
LEFT JOIN sys.dm_exec_connections c ON c.session_id = s.spid
WHERE (s.spid IS NULL OR c.session_id >= 51)
GROUP BY d.Name

Azure SQL

Expand Down
2 changes: 1 addition & 1 deletion build/versions.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<MajorVersion>1</MajorVersion>
<MinorVersion>0</MinorVersion>
<PatchVersion>6</PatchVersion>
<PatchVersion>7</PatchVersion>

<BuildQuality></BuildQuality>
</PropertyGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/Common/CommonAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.0.6")]
[assembly: AssemblyFileVersion("1.0.6")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyVersion("1.0.7")]
[assembly: AssemblyFileVersion("1.0.7")]
[assembly: AssemblyInformationalVersion("1.0.7")]
19 changes: 12 additions & 7 deletions src/NewRelic.Microsoft.SqlServer.Plugin/Configuration/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Data.SqlClient;
using System;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
Expand Down Expand Up @@ -34,7 +34,7 @@ public Settings(ISqlEndpoint[] endpoints)
public int PollIntervalSeconds { get; set; }
public string ServiceName { get; set; }
public ISqlEndpoint[] Endpoints { get; private set; }
public bool CollectOnly { get; set; }
public bool TestMode { get; set; }
public bool IsProcessElevated { get; private set; }

public string Version
Expand Down Expand Up @@ -79,11 +79,16 @@ internal static Settings FromConfigurationSection(NewRelicConfigurationSection s

public void ToLog(ILog log)
{
// Pending review by New Relic before adding this information
// log.Info(" New Relic Key: " + LicenseKey);

log.Info(" Version: " + Version);
log.Info(" PollIntervalSeconds: " + PollIntervalSeconds);
log.Info(" CollectOnly: " + CollectOnly);
log.Info(" RunAsAdministrator: " + IsProcessElevated);
log.Info(" TotalEndpoints: " + Endpoints.Length);
log.Info(" Test Mode: " + (TestMode ? "Yes" : "No"));
log.Info(" Windows Service: " + (Environment.UserInteractive ? "No" : "Yes"));
log.InfoFormat(@" User: {0}\{1}", Environment.UserDomainName, Environment.UserName);
log.Info(" Run as Administrator: " + (IsProcessElevated ? "Yes" : "No"));
log.Info(" Total Endpoints: " + Endpoints.Length);
log.Info(" Poll Interval Seconds: " + PollIntervalSeconds);

var sqlServerEndpoints = Endpoints.OfType<SqlServerEndpoint>().ToArray();
if (sqlServerEndpoints.Any())
Expand Down Expand Up @@ -118,7 +123,7 @@ public void ToLog(ILog log)

public override string ToString()
{
return string.Format("Version: {0}, PollIntervalSeconds: {1}, CollectOnly: {2}", Version, PollIntervalSeconds, CollectOnly);
return string.Format("Version: {0}, PollIntervalSeconds: {1}, TestMode: {2}", Version, PollIntervalSeconds, TestMode);
}
}
}
4 changes: 2 additions & 2 deletions src/NewRelic.Microsoft.SqlServer.Plugin/Core/SqlPoller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public void Start()
var queries = new QueryLocator(new DapperWrapper()).PrepareQueries();
_settings.Endpoints.ForEach(e => e.SetQueries(queries));

var initialDelaySeconds = _settings.CollectOnly ? 0 : _settings.PollIntervalSeconds;
var initialDelaySeconds = _settings.TestMode ? 0 : _settings.PollIntervalSeconds;
var pollingThreadSettings = new PollingThreadSettings
{
Name = "SqlPoller",
// Set to immediate when CollectOnly for instant gratification
// Set to immediate when in TestMode for instant gratification.
InitialPollDelaySeconds = initialDelaySeconds,
PollIntervalSeconds = _settings.PollIntervalSeconds,
PollAction = () => _metricCollector.QueryEndpoints(queries),
Expand Down
2 changes: 1 addition & 1 deletion src/NewRelic.Microsoft.SqlServer.Plugin/MetricCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ internal void SendComponentDataToCollector(ISqlEndpoint endpoint)
var platformData = endpoint.GeneratePlatformData(_agentData);

// Allows a testing mode that does not send data to New Relic
if (_settings.CollectOnly)
if (_settings.TestMode)
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<Compile Include="QueryTypes\SqlDmlActivity.cs" />
<Compile Include="QueryTypes\MemoryViewNuma.cs" />
<Compile Include="SqlEndpointBase.cs" />
<Compile Include="SqlErrorReporter.cs" />
<Compile Include="SqlServerEndpoint.cs" />
<Compile Include="Configuration\SqlServerCollection.cs" />
<Compile Include="Configuration\SqlServerElement.cs" />
Expand Down
40 changes: 31 additions & 9 deletions src/NewRelic.Microsoft.SqlServer.Plugin/Options.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using CommandLine;
using System;

using CommandLine;

using NewRelic.Microsoft.SqlServer.Plugin.Properties;

Expand All @@ -13,27 +15,47 @@ public class Options
MutuallyExclusiveSet = "mode")]
public bool Install { get; set; }

[Option('u', "uninstall", HelpText = "Attempts to Stop and Uninstall the " + ServiceConstants.DisplayName + " Windows service from this machine", MutuallyExclusiveSet = "mode")]
[Option('u', "uninstall", HelpText = "Attempts to Stop and Uninstall the " + ServiceConstants.DisplayName + " Windows service from this machine.", MutuallyExclusiveSet = "mode")]
public bool Uninstall { get; set; }

[Option('s', "start", HelpText = "Attempts to Start the " + ServiceConstants.DisplayName + " Windows service on this machine", MutuallyExclusiveSet = "mode")]
[Option('s', "start", HelpText = "Attempts to Start the " + ServiceConstants.DisplayName + " Windows service on this machine.", MutuallyExclusiveSet = "mode")]
public bool Start { get; set; }

[Option("stop", HelpText = "Attempts to Stop the " + ServiceConstants.DisplayName + " Windows service on this machine", MutuallyExclusiveSet = "mode")]
[Option("stop", HelpText = "Attempts to Stop the " + ServiceConstants.DisplayName + " Windows service on this machine.", MutuallyExclusiveSet = "mode")]
public bool Stop { get; set; }

[Option("installorstart",
HelpText = "If the service was not previously installed it installs it, but does not start it. Alternatively if the service is already installed it simply starts the service",
HelpText = "If the service was not previously installed it installs it, but does not start it. Alternatively if the service is already installed it simply starts the service.",
MutuallyExclusiveSet = "mode")]
public bool InstallOrStart { get; set; }

[Option("service-name", HelpText = "Optional. Overrides the default service name when installing or uninstalling")]
[Option("service-name", HelpText = "Optional. Overrides the default service name when installing or uninstalling.")]
public string ServiceName { get; set; }

[Option("collect-only", DefaultValue = false, HelpText = "Collect metrics, but do not send them to New Relic")]
public bool CollectOnly { get; set; }
[Option('t', "test", DefaultValue = false, HelpText = "Verifies the configuration. Collect metrics locally but does not send them to New Relic.")]
public bool TestMode { get; set; }

[Option("config-file", HelpText = "Specify that settings are in a different file than the .exe.config")]
[Option("config-file", HelpText = "Specify that settings are in a different file than the .exe.config.")]
public string ConfigFile { get; set; }

public static Options ParseArguments(string[] args)
{
// '--test' arg was once named '--collect-only'. This provides backwards compatibility.
if (args != null)
{
var index = Array.IndexOf(args, "--collect-only");
if (index >= 0)
{
args[index] = "--test";
}
}

var options = new Options();

// If bad args were passed, will exit and print usage
Parser.Default.ParseArgumentsStrict(args, options);

return options;
}
}
}
16 changes: 3 additions & 13 deletions src/NewRelic.Microsoft.SqlServer.Plugin/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
using System.ServiceProcess;
using System.Threading;

using CommandLine;

using NewRelic.Microsoft.SqlServer.Plugin.Configuration;
using NewRelic.Microsoft.SqlServer.Plugin.Core;
using NewRelic.Microsoft.SqlServer.Plugin.Core.Extensions;
Expand All @@ -24,12 +22,9 @@ private static int Main(string[] args)
{
try
{
var options = new Options();

var log = SetUpLogConfig();

// If bad args were passed, will exit and print usage
Parser.Default.ParseArgumentsStrict(args, options);
var options = Options.ParseArguments(args);

var settings = ConfigurationParser.ParseSettings(log, options.ConfigFile);
Settings.Default = settings;
Expand Down Expand Up @@ -60,8 +55,8 @@ private static int Main(string[] args)
else
{
Thread.CurrentThread.Name = "Main";
settings.CollectOnly = options.CollectOnly;
log.InfoFormat("New Relic® Sql Server Plugin");
settings.TestMode = options.TestMode;
log.InfoFormat("New Relic Sql Server Plugin");
log.Info("Loaded Settings:");
settings.ToLog(log);

Expand All @@ -72,7 +67,6 @@ private static int Main(string[] args)

if (Environment.UserInteractive)
{
Console.Out.WriteLine("Starting Interactive mode");
RunInteractive(settings);
}
else
Expand Down Expand Up @@ -114,8 +108,6 @@ public static ILog SetUpLogConfig()
/// <param name="settings"></param>
private static void RunInteractive(Settings settings)
{
Console.Out.WriteLine("Starting Server");

// Start our services
var poller = new SqlPoller(settings);
poller.Start();
Expand All @@ -132,8 +124,6 @@ private static void RunInteractive(Settings settings)
key = consoleKeyInfo.KeyChar;
} while (key != 'q' && key != 'Q');

Console.Out.WriteLine("Stopping...");

// Stop our services
poller.Stop();

Expand Down
64 changes: 1 addition & 63 deletions src/NewRelic.Microsoft.SqlServer.Plugin/SqlEndpointBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,69 +287,7 @@ private void LogErrorSummary(ILog log, Exception e, ISqlQuery query)
var sqlException = e.InnerException as SqlException;
if (sqlException == null) return;

var connectionString = new SqlConnectionStringBuilder(ConnectionString);

switch (sqlException.Number)
{
case 297: // User cannot log on via Windows Auth
case 18456: // User cannot login via SQL Auth
if (connectionString.IntegratedSecurity)
{
// System.Data.SqlClient.SqlException: Login failed. The login is from an untrusted domain and cannot be used with Windows authentication.
log.ErrorFormat("The Windows service is running as user '{0}', however, the user cannot access the server '{1}'. " +
"Consider changing the connection string in the configuration file " +
"or adding permissions to your SQL Server (see readme.md).",
Environment.UserName, connectionString.DataSource);
}
else
{
// System.Data.SqlClient.SqlException: Login failed for user '<user id>'.
log.ErrorFormat("User '{0}' cannot access the server '{1}'. " +
"Consider changing the connection string in the configuration file " +
"or adding permissions to your SQL Server (see readme.md).",
connectionString.UserID, connectionString.DataSource);
}
break;

case 4060: // Missing database user
// System.Data.SqlClient.SqlException: Cannot open database "Junk" requested by the login. The login failed.
if (connectionString.IntegratedSecurity)
{
log.ErrorFormat("The Windows service is running as user '{0}', however, the user cannot access the database '{1}'. " +
"Ensure the login has a user in the database (see readme.md).",
Environment.UserName, connectionString.InitialCatalog);
}
else
{
log.ErrorFormat("User '{0}' cannot access the database '{1}'. " +
"Ensure the login has a user in the database (see readme.md).",
connectionString.UserID, connectionString.InitialCatalog);
}
break;

case 10060:
case 10061:
case 11001:
case 40615:
if (sqlException.Message.Contains("sp_set_firewall_rule"))
{
var relevantErrorMessage = Regex.Replace(sqlException.Message, @"change to take effect\.(.*)$", string.Empty, RegexOptions.Singleline);
log.Error("Azure SQL Error: " + relevantErrorMessage);
}
else
{
log.ErrorFormat("Timeout connecting to server at '{0}'. Verify that the connection string is correct and the server is reachable.",
connectionString.DataSource);
}
break;

default:
// System.Data.SqlClient.SqlException: Arithmetic overflow error for data type tinyint, value = -34.
// System.Data.SqlClient.SqlException: Arithmetic overflow error converting expression to data type int.
log.ErrorFormat("Error collecting metric '{0}'. Contact New Relic support at https://support.newrelic.com/home. Details: C{1}, M{2}, S{3}",
query.QueryName, sqlException.Class, sqlException.Number, sqlException.State);
break;
}
log.LogSqlException(sqlException, query, ConnectionString);
}
}
}
Loading

0 comments on commit 61bed40

Please sign in to comment.