mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-16 10:03:51 -07:00
Added Logentries to track down automatic upgrade issues
This commit is contained in:
parent
427b102900
commit
493a3c9724
17 changed files with 3106 additions and 245 deletions
648
src/LogentriesCore/AsyncLogger.cs
Normal file
648
src/LogentriesCore/AsyncLogger.cs
Normal file
|
@ -0,0 +1,648 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace LogentriesCore
|
||||
{
|
||||
public class AsyncLogger
|
||||
{
|
||||
#region Constants
|
||||
|
||||
// Current version number.
|
||||
protected const String Version = "2.6.0";
|
||||
|
||||
// Size of the internal event queue.
|
||||
protected const int QueueSize = 32768;
|
||||
|
||||
// Minimal delay between attempts to reconnect in milliseconds.
|
||||
protected const int MinDelay = 100;
|
||||
|
||||
// Maximal delay between attempts to reconnect in milliseconds.
|
||||
protected const int MaxDelay = 10000;
|
||||
|
||||
// Appender signature - used for debugging messages.
|
||||
protected const String LeSignature = "LE: ";
|
||||
|
||||
// Legacy Logentries configuration names.
|
||||
protected const String LegacyConfigTokenName = "LOGENTRIES_TOKEN";
|
||||
protected const String LegacyConfigAccountKeyName = "LOGENTRIES_ACCOUNT_KEY";
|
||||
protected const String LegacyConfigLocationName = "LOGENTRIES_LOCATION";
|
||||
|
||||
// New Logentries configuration names.
|
||||
protected const String ConfigTokenName = "Logentries.Token";
|
||||
protected const String ConfigAccountKeyName = "Logentries.AccountKey";
|
||||
protected const String ConfigLocationName = "Logentries.Location";
|
||||
|
||||
// Error message displayed when invalid token is detected.
|
||||
protected const String InvalidTokenMessage = "\n\nIt appears your LOGENTRIES_TOKEN value is invalid or missing.\n\n";
|
||||
|
||||
// Error message displayed when invalid account_key or location parameters are detected.
|
||||
protected const String InvalidHttpPutCredentialsMessage = "\n\nIt appears your LOGENTRIES_ACCOUNT_KEY or LOGENTRIES_LOCATION values are invalid or missing.\n\n";
|
||||
|
||||
// Error message deisplayed when queue overflow occurs.
|
||||
protected const String QueueOverflowMessage = "\n\nLogentries buffer queue overflow. Message dropped.\n\n";
|
||||
|
||||
// Newline char to trim from message for formatting.
|
||||
protected static char[] TrimChars = { '\r', '\n' };
|
||||
|
||||
/** Non-Unix and Unix Newline */
|
||||
protected static string[] posix_newline = { "\r\n", "\n" };
|
||||
|
||||
/** Unicode line separator character */
|
||||
protected static string line_separator = "\u2028";
|
||||
|
||||
// Restricted symbols that should not appear in host name.
|
||||
// See http://support.microsoft.com/kb/228275/en-us for details.
|
||||
private static Regex ForbiddenHostNameChars = new Regex(@"[/\\\[\]\""\:\;\|\<\>\+\=\,\?\* _]{1,}", RegexOptions.Compiled);
|
||||
|
||||
/** Regex used to validate GUID in .NET3.5 */
|
||||
private static Regex isGuid = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Singletons
|
||||
|
||||
// UTF-8 output character set.
|
||||
protected static readonly UTF8Encoding UTF8 = new UTF8Encoding();
|
||||
|
||||
// ASCII character set used by HTTP.
|
||||
protected static readonly ASCIIEncoding ASCII = new ASCIIEncoding();
|
||||
|
||||
//static list of all the queues the le appender might be managing.
|
||||
private static ConcurrentBag<BlockingCollection<string>> _allQueues = new ConcurrentBag<BlockingCollection<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the queue is empty after waiting the specified waitTime.
|
||||
/// Returns true or false if the underlying queues are empty.
|
||||
/// </summary>
|
||||
/// <param name="waitTime">The length of time the method should block before giving up waiting for it to empty.</param>
|
||||
/// <returns>True if the queue is empty, false if there are still items waiting to be written.</returns>
|
||||
public static bool AreAllQueuesEmpty(TimeSpan waitTime)
|
||||
{
|
||||
var start = DateTime.UtcNow;
|
||||
var then = DateTime.UtcNow;
|
||||
|
||||
while (start.Add(waitTime) > then)
|
||||
{
|
||||
if (_allQueues.All(x => x.Count == 0))
|
||||
return true;
|
||||
|
||||
Thread.Sleep(100);
|
||||
then = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
return _allQueues.All(x => x.Count == 0);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public AsyncLogger()
|
||||
{
|
||||
Queue = new BlockingCollection<string>(QueueSize);
|
||||
_allQueues.Add(Queue);
|
||||
|
||||
WorkerThread = new Thread(Run);
|
||||
WorkerThread.Name = "Logentries Log4net Appender";
|
||||
WorkerThread.IsBackground = true;
|
||||
}
|
||||
|
||||
#region Configuration properties
|
||||
|
||||
private String m_Token = "";
|
||||
private String m_AccountKey = "";
|
||||
private String m_Location = "";
|
||||
private bool m_ImmediateFlush = false;
|
||||
private bool m_Debug = false;
|
||||
private bool m_UseHttpPut = false;
|
||||
private bool m_UseSsl = false;
|
||||
|
||||
// Properties for defining location of DataHub instance if one is used.
|
||||
private bool m_UseDataHub = false; // By default Logentries service is used instead of DataHub instance.
|
||||
private String m_DataHubAddr = "";
|
||||
private int m_DataHubPort = 0;
|
||||
|
||||
// Properties to define host name of user's machine and define user-specified log ID.
|
||||
private bool m_UseHostName = false; // Defines whether to prefix log message with HostName or not.
|
||||
private String m_HostName = ""; // User-defined or auto-defined host name (if not set in config. file)
|
||||
private String m_LogID = ""; // User-defined log ID to be prefixed to the log message.
|
||||
|
||||
// Sets DataHub usage flag.
|
||||
public void setIsUsingDataHub(bool useDataHub)
|
||||
{
|
||||
m_UseDataHub = useDataHub;
|
||||
}
|
||||
|
||||
public bool getIsUsingDataHab()
|
||||
{
|
||||
return m_UseDataHub;
|
||||
}
|
||||
|
||||
// Sets DataHub instance address.
|
||||
public void setDataHubAddr(String dataHubAddr)
|
||||
{
|
||||
m_DataHubAddr = dataHubAddr;
|
||||
}
|
||||
|
||||
public String getDataHubAddr()
|
||||
{
|
||||
return m_DataHubAddr;
|
||||
}
|
||||
|
||||
// Sets the port on which DataHub instance is waiting for log messages.
|
||||
public void setDataHubPort(int port)
|
||||
{
|
||||
m_DataHubPort = port;
|
||||
}
|
||||
|
||||
public int getDataHubPort()
|
||||
{
|
||||
return m_DataHubPort;
|
||||
}
|
||||
|
||||
public void setToken(String token)
|
||||
{
|
||||
m_Token = token;
|
||||
}
|
||||
|
||||
public String getToken()
|
||||
{
|
||||
return m_Token;
|
||||
}
|
||||
|
||||
public void setAccountKey(String accountKey)
|
||||
{
|
||||
m_AccountKey = accountKey;
|
||||
}
|
||||
|
||||
public string getAccountKey()
|
||||
{
|
||||
return m_AccountKey;
|
||||
}
|
||||
|
||||
public void setLocation(String location)
|
||||
{
|
||||
m_Location = location;
|
||||
}
|
||||
|
||||
public String getLocation()
|
||||
{
|
||||
return m_Location;
|
||||
}
|
||||
|
||||
public void setImmediateFlush(bool immediateFlush)
|
||||
{
|
||||
m_ImmediateFlush = immediateFlush;
|
||||
}
|
||||
|
||||
public bool getImmediateFlush()
|
||||
{
|
||||
return m_ImmediateFlush;
|
||||
}
|
||||
|
||||
public void setDebug(bool debug)
|
||||
{
|
||||
m_Debug = debug;
|
||||
}
|
||||
|
||||
public bool getDebug()
|
||||
{
|
||||
return m_Debug;
|
||||
}
|
||||
|
||||
public void setUseHttpPut(bool useHttpPut)
|
||||
{
|
||||
m_UseHttpPut = useHttpPut;
|
||||
}
|
||||
|
||||
public bool getUseHttpPut()
|
||||
{
|
||||
return m_UseHttpPut;
|
||||
}
|
||||
|
||||
public void setUseSsl(bool useSsl)
|
||||
{
|
||||
m_UseSsl = useSsl;
|
||||
}
|
||||
|
||||
public bool getUseSsl()
|
||||
{
|
||||
return m_UseSsl;
|
||||
}
|
||||
|
||||
public void setUseHostName(bool useHostName)
|
||||
{
|
||||
m_UseHostName = useHostName;
|
||||
}
|
||||
|
||||
public bool getUseHostName()
|
||||
{
|
||||
return m_UseHostName;
|
||||
}
|
||||
|
||||
public void setHostName(String hostName)
|
||||
{
|
||||
m_HostName = hostName;
|
||||
}
|
||||
|
||||
public String getHostName()
|
||||
{
|
||||
return m_HostName;
|
||||
}
|
||||
|
||||
public void setLogID(String logID)
|
||||
{
|
||||
m_LogID = logID;
|
||||
}
|
||||
|
||||
public String getLogID()
|
||||
{
|
||||
return m_LogID;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected readonly BlockingCollection<string> Queue;
|
||||
protected readonly Thread WorkerThread;
|
||||
protected readonly Random Random = new Random();
|
||||
|
||||
private LeClient LeClient = null;
|
||||
protected bool IsRunning = false;
|
||||
|
||||
#region Protected methods
|
||||
|
||||
protected virtual void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open connection.
|
||||
ReopenConnection();
|
||||
|
||||
string logMessagePrefix = String.Empty;
|
||||
|
||||
if (m_UseHostName)
|
||||
{
|
||||
// If LogHostName is set to "true", but HostName is not defined -
|
||||
// try to get host name from Environment.
|
||||
if (m_HostName == String.Empty)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteDebugMessages("HostName parameter is not defined - trying to get it from System.Environment.MachineName");
|
||||
m_HostName = "HostName=" + System.Environment.MachineName + " ";
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
// Cannot get host name automatically, so assume that HostName is not used
|
||||
// and log message is sent without it.
|
||||
m_UseHostName = false;
|
||||
WriteDebugMessages("Failed to get HostName parameter using System.Environment.MachineName. Log messages will not be prefixed by HostName");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CheckIfHostNameValid(m_HostName))
|
||||
{
|
||||
// If user-defined host name is incorrect - we cannot use it
|
||||
// and log message is sent without it.
|
||||
m_UseHostName = false;
|
||||
WriteDebugMessages("HostName parameter contains prohibited characters. Log messages will not be prefixed by HostName");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HostName = "HostName=" + m_HostName + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_LogID != String.Empty)
|
||||
{
|
||||
logMessagePrefix = m_LogID + " ";
|
||||
}
|
||||
|
||||
if (m_UseHostName)
|
||||
{
|
||||
logMessagePrefix += m_HostName;
|
||||
}
|
||||
|
||||
// Flag that is set if logMessagePrefix is empty.
|
||||
bool isPrefixEmpty = (logMessagePrefix == String.Empty);
|
||||
|
||||
// Send data in queue.
|
||||
while (true)
|
||||
{
|
||||
// Take data from queue.
|
||||
var line = Queue.Take();
|
||||
|
||||
// Replace newline chars with line separator to format multi-line events nicely.
|
||||
foreach (String newline in posix_newline)
|
||||
{
|
||||
line = line.Replace(newline, line_separator);
|
||||
}
|
||||
|
||||
// If m_UseDataHub == true (logs are sent to DataHub instance) then m_Token is not
|
||||
// appended to the message.
|
||||
string finalLine = ((!m_UseHttpPut && !m_UseDataHub) ? this.m_Token + line : line) + '\n';
|
||||
|
||||
// Add prefixes: LogID and HostName if they are defined.
|
||||
if (!isPrefixEmpty)
|
||||
{
|
||||
finalLine = logMessagePrefix + finalLine;
|
||||
}
|
||||
|
||||
byte[] data = UTF8.GetBytes(finalLine);
|
||||
|
||||
// Send data, reconnect if needed.
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.LeClient.Write(data, 0, data.Length);
|
||||
|
||||
if (m_ImmediateFlush)
|
||||
this.LeClient.Flush();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Reopen the lost connection.
|
||||
ReopenConnection();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ThreadInterruptedException ex)
|
||||
{
|
||||
WriteDebugMessages("Logentries asynchronous socket client was interrupted.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OpenConnection()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LeClient == null)
|
||||
{
|
||||
// Create LeClient instance providing all needed parameters. If DataHub-related properties
|
||||
// have not been overridden by log4net or NLog configurators, then DataHub is not used,
|
||||
// because m_UseDataHub == false by default.
|
||||
LeClient = new LeClient(m_UseHttpPut, m_UseSsl, m_UseDataHub, m_DataHubAddr, m_DataHubPort);
|
||||
}
|
||||
|
||||
LeClient.Connect();
|
||||
|
||||
if (m_UseHttpPut)
|
||||
{
|
||||
var header = String.Format("PUT /{0}/hosts/{1}/?realtime=1 HTTP/1.1\r\n\r\n", m_AccountKey, m_Location);
|
||||
LeClient.Write(ASCII.GetBytes(header), 0, header.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new IOException("An error occurred while opening the connection.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ReopenConnection()
|
||||
{
|
||||
CloseConnection();
|
||||
|
||||
var rootDelay = MinDelay;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenConnection();
|
||||
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (m_Debug)
|
||||
{
|
||||
WriteDebugMessages("Unable to connect to Logentries API.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
rootDelay *= 2;
|
||||
if (rootDelay > MaxDelay)
|
||||
rootDelay = MaxDelay;
|
||||
|
||||
var waitFor = rootDelay + Random.Next(rootDelay);
|
||||
|
||||
try
|
||||
{
|
||||
Thread.Sleep(waitFor);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new ThreadInterruptedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CloseConnection()
|
||||
{
|
||||
if (LeClient != null)
|
||||
LeClient.Close();
|
||||
}
|
||||
|
||||
public static bool IsNullOrWhiteSpace(String value)
|
||||
{
|
||||
if (value == null) return true;
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
if (!Char.IsWhiteSpace(value[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string retrieveSetting(String name)
|
||||
{
|
||||
string value;
|
||||
|
||||
|
||||
value = ConfigurationManager.AppSettings[name];
|
||||
|
||||
if (IsNullOrWhiteSpace(value))
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Environment.GetEnvironmentVariable(name);
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use CloudConfigurationManager with .NET4.0 and fallback to System.Configuration for previous frameworks.
|
||||
*
|
||||
* NOTE: This is not entirely clear with regards to the above comment, but this block of code uses a compiler directive NET4_0
|
||||
* which is not set by default anywhere, so most uses of this code will default back to the "pre-.Net4.0" code branch, even
|
||||
* if you are using .Net4.0 or .Net4.5.
|
||||
*
|
||||
* The second issue is that there are two appsetting keys for each setting - the "legacy" key, such as "LOGENTRIES_TOKEN"
|
||||
* and the "non-legacy" key, such as "Logentries.Token". Again, I'm not sure of the reasons behind this, so the code below checks
|
||||
* both the legacy and non-legacy keys, defaulting to the legacy keys if they are found.
|
||||
*
|
||||
* It probably should be investigated whether the fallback to ConfigurationManager is needed at all, as CloudConfigurationManager
|
||||
* will retrieve settings from appSettings in a non-Azure environment.
|
||||
*/
|
||||
protected virtual bool LoadCredentials()
|
||||
{
|
||||
if (!m_UseHttpPut)
|
||||
{
|
||||
if (GetIsValidGuid(m_Token))
|
||||
return true;
|
||||
|
||||
var configToken = retrieveSetting(LegacyConfigTokenName) ?? retrieveSetting(ConfigTokenName);
|
||||
|
||||
if (!String.IsNullOrEmpty(configToken) && GetIsValidGuid(configToken))
|
||||
{
|
||||
m_Token = configToken;
|
||||
return true;
|
||||
}
|
||||
|
||||
WriteDebugMessages(InvalidTokenMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_AccountKey != "" && GetIsValidGuid(m_AccountKey) && m_Location != "")
|
||||
return true;
|
||||
|
||||
var configAccountKey = ConfigurationManager.AppSettings[LegacyConfigAccountKeyName] ?? ConfigurationManager.AppSettings[ConfigAccountKeyName];
|
||||
if (!String.IsNullOrEmpty(configAccountKey) && GetIsValidGuid(configAccountKey))
|
||||
{
|
||||
m_AccountKey = configAccountKey;
|
||||
|
||||
var configLocation = ConfigurationManager.AppSettings[LegacyConfigLocationName] ?? ConfigurationManager.AppSettings[ConfigLocationName];
|
||||
if (!String.IsNullOrEmpty(configLocation))
|
||||
{
|
||||
m_Location = configLocation;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
WriteDebugMessages(InvalidHttpPutCredentialsMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckIfHostNameValid(String hostName)
|
||||
{
|
||||
return !ForbiddenHostNameChars.IsMatch(hostName); // Returns false if reg.ex. matches any of forbidden chars.
|
||||
}
|
||||
|
||||
static bool IsGuid(string candidate, out Guid output)
|
||||
{
|
||||
bool isValid = false;
|
||||
output = Guid.Empty;
|
||||
|
||||
if (isGuid.IsMatch(candidate))
|
||||
{
|
||||
output = new Guid(candidate);
|
||||
isValid = true;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
protected virtual bool GetIsValidGuid(string guidString)
|
||||
{
|
||||
if (String.IsNullOrEmpty(guidString))
|
||||
return false;
|
||||
|
||||
System.Guid newGuid = System.Guid.NewGuid();
|
||||
return IsGuid(guidString, out newGuid);
|
||||
}
|
||||
|
||||
protected virtual void WriteDebugMessages(string message, Exception ex)
|
||||
{
|
||||
if (!m_Debug)
|
||||
return;
|
||||
|
||||
message = LeSignature + message;
|
||||
string[] messages = { message, ex.ToString() };
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
// Use below line instead when compiling with log4net1.2.10.
|
||||
//LogLog.Debug(msg);
|
||||
|
||||
//LogLog.Debug(typeof(LogentriesAppender), msg);
|
||||
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void WriteDebugMessages(string message)
|
||||
{
|
||||
if (!m_Debug)
|
||||
return;
|
||||
|
||||
message = LeSignature + message;
|
||||
|
||||
// Use below line instead when compiling with log4net1.2.10.
|
||||
//LogLog.Debug(message);
|
||||
|
||||
//LogLog.Debug(typeof(LogentriesAppender), message);
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region publicMethods
|
||||
|
||||
public virtual void AddLine(string line)
|
||||
{
|
||||
if (!IsRunning)
|
||||
{
|
||||
// We need to load user credentials only
|
||||
// if the configuration does not state that DataHub is used;
|
||||
// credentials needed only if logs are sent to LE service directly.
|
||||
bool credentialsLoaded = false;
|
||||
if(!m_UseDataHub)
|
||||
{
|
||||
credentialsLoaded = LoadCredentials();
|
||||
}
|
||||
|
||||
// If in DataHub mode credentials are ignored.
|
||||
if (credentialsLoaded || m_UseDataHub)
|
||||
{
|
||||
WriteDebugMessages("Starting Logentries asynchronous socket client.");
|
||||
WorkerThread.Start();
|
||||
IsRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
WriteDebugMessages("Queueing: " + line);
|
||||
|
||||
String trimmedEvent = line.TrimEnd(TrimChars);
|
||||
|
||||
// Try to append data to queue.
|
||||
if (!Queue.TryAdd(trimmedEvent))
|
||||
{
|
||||
Queue.Take();
|
||||
if (!Queue.TryAdd(trimmedEvent))
|
||||
WriteDebugMessages(QueueOverflowMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public void interruptWorker()
|
||||
{
|
||||
WorkerThread.Interrupt();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
103
src/LogentriesCore/LeClient.cs
Normal file
103
src/LogentriesCore/LeClient.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace LogentriesCore
|
||||
{
|
||||
class LeClient
|
||||
{
|
||||
// Logentries API server address.
|
||||
protected const String LeApiUrl = "api.logentries.com";
|
||||
|
||||
// Port number for token logging on Logentries API server.
|
||||
protected const int LeApiTokenPort = 10000;
|
||||
|
||||
// Port number for TLS encrypted token logging on Logentries API server
|
||||
protected const int LeApiTokenTlsPort = 20000;
|
||||
|
||||
// Port number for HTTP PUT logging on Logentries API server.
|
||||
protected const int LeApiHttpPort = 80;
|
||||
|
||||
// Port number for SSL HTTP PUT logging on Logentries API server.
|
||||
protected const int LeApiHttpsPort = 443;
|
||||
|
||||
// Creates LeClient instance. If do not define useServerUrl and/or useOverrideProt during call
|
||||
// LeClient will be configured to work with api.logentries.com server; otherwise - with
|
||||
// defined server on defined port.
|
||||
public LeClient(bool useHttpPut, bool useSsl, bool useDataHub, String serverAddr, int port)
|
||||
{
|
||||
|
||||
// Override port number and server address to send logs to DataHub instance.
|
||||
if (useDataHub)
|
||||
{
|
||||
m_UseSsl = false; // DataHub does not support receiving log messages over SSL for now.
|
||||
m_TcpPort = port;
|
||||
m_ServerAddr = serverAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_UseSsl = useSsl;
|
||||
|
||||
if (!m_UseSsl)
|
||||
m_TcpPort = useHttpPut ? LeApiHttpPort : LeApiTokenPort;
|
||||
else
|
||||
m_TcpPort = useHttpPut ? LeApiHttpsPort : LeApiTokenTlsPort;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_UseSsl = false;
|
||||
private int m_TcpPort;
|
||||
private TcpClient m_Client = null;
|
||||
private Stream m_Stream = null;
|
||||
private SslStream m_SslStream = null;
|
||||
private String m_ServerAddr = LeApiUrl; // By default m_ServerAddr points to api.logentries.com if useDataHub is not set to true.
|
||||
|
||||
private Stream ActiveStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_UseSsl ? m_SslStream : m_Stream;
|
||||
}
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
m_Client = new TcpClient(m_ServerAddr, m_TcpPort);
|
||||
m_Client.NoDelay = true;
|
||||
|
||||
m_Stream = m_Client.GetStream();
|
||||
|
||||
if (m_UseSsl)
|
||||
{
|
||||
m_SslStream = new SslStream(m_Stream);
|
||||
m_SslStream.AuthenticateAsClient(m_ServerAddr);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
ActiveStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
ActiveStream.Flush();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (m_Client != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Client.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
92
src/LogentriesCore/LogentriesCore.csproj
Normal file
92
src/LogentriesCore/LogentriesCore.csproj
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{90D6E9FC-7B88-4E1B-B018-8FA742274558}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LogentriesCore</RootNamespace>
|
||||
<AssemblyName>LogentriesCore</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AsyncLogger.cs" />
|
||||
<Compile Include="LeClient.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>if $(ConfigurationName) == Release (
|
||||
COPY "$(TargetPath)" "%25NUGET_PROJECTS%25$(ProjectName)\2.6.0\lib\net40\" /Y
|
||||
nuget pack %25NUGET_PROJECTS%25$(ProjectName)\2.6.0\logentries.core.nuspec /o %25NUGET_PROJECTS%25$(ProjectName)\2.6.0\
|
||||
)</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
36
src/LogentriesCore/Properties/AssemblyInfo.cs
Normal file
36
src/LogentriesCore/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("LogentriesCore")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("LogentriesCore")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("14055980-6937-4745-9449-dabf47c1d892")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.6.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.6.0.0")]
|
4
src/LogentriesCore/packages.config
Normal file
4
src/LogentriesCore/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="2.0.1.0" targetFramework="net40" />
|
||||
</packages>
|
Loading…
Add table
Add a link
Reference in a new issue