|
|
|
|
using NLog;
|
|
|
|
|
using NLog.Config;
|
|
|
|
|
using NLog.Targets;
|
|
|
|
|
using NLog.Targets.Wrappers;
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using Common.Logging;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using NLog.Layouts;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using NLog.RestTarget;
|
|
|
|
|
using NLog.Common;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using POSV.Utils;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using POSV;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using POSV.Common.Util;
|
|
|
|
|
|
|
|
|
|
namespace POSV.Utils
|
|
|
|
|
{
|
|
|
|
|
public static class NLogUtils
|
|
|
|
|
{
|
|
|
|
|
private static object locker = new object();
|
|
|
|
|
|
|
|
|
|
static NLogUtils()
|
|
|
|
|
{
|
|
|
|
|
string dataDirFullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory , @"data/backup/");
|
|
|
|
|
if (!Directory.Exists(dataDirFullPath))
|
|
|
|
|
{
|
|
|
|
|
lock (locker)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(dataDirFullPath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(dataDirFullPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string logDirFullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory , @"logs/");
|
|
|
|
|
|
|
|
|
|
if (!Directory.Exists(logDirFullPath))
|
|
|
|
|
{
|
|
|
|
|
lock (locker)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(logDirFullPath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(logDirFullPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//zhangy 2020-02-19 Edit 修改日志文件输出格式,添加按日归档、保留7日模式
|
|
|
|
|
static void nlog_to_file_and_archive(LoggingConfiguration config)
|
|
|
|
|
{
|
|
|
|
|
var fileTarget = new FileTarget
|
|
|
|
|
{
|
|
|
|
|
MaxArchiveFiles = 7,
|
|
|
|
|
FileName = "${basedir}/logs/log.txt" ,
|
|
|
|
|
Layout = Layout.FromString("${longdate} | [${uppercase:${level}}] | ${logger} | ${message} | ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}") ,
|
|
|
|
|
ArchiveFileName = "${basedir}/logs/archives/log${shortdate}.{#####}.txt",
|
|
|
|
|
ArchiveEvery = FileArchivePeriod.Day,
|
|
|
|
|
ArchiveNumbering = ArchiveNumberingMode.Rolling,
|
|
|
|
|
EnableArchiveFileCompression = true,
|
|
|
|
|
ConcurrentWrites = true,
|
|
|
|
|
KeepFileOpen = false,
|
|
|
|
|
AutoFlush = true,
|
|
|
|
|
DeleteOldFileOnStartup = false,
|
|
|
|
|
CreateDirs = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AsyncTargetWrapper wrapper = new AsyncTargetWrapper();
|
|
|
|
|
wrapper.WrappedTarget = fileTarget;
|
|
|
|
|
wrapper.QueueLimit = 5000;
|
|
|
|
|
wrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard;
|
|
|
|
|
|
|
|
|
|
// Adding "File" as one of the log targets
|
|
|
|
|
config.AddTarget("file" , wrapper);
|
|
|
|
|
|
|
|
|
|
config.LoggingRules.Add(new NLog.Config.LoggingRule("*" , NLog.LogLevel.Info , wrapper));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nlog_to_console(LoggingConfiguration config)
|
|
|
|
|
{
|
|
|
|
|
var consoleTarget = new ColoredConsoleTarget
|
|
|
|
|
{
|
|
|
|
|
Layout = Layout.FromString("${longdate} | [${uppercase:${level}}] | ${logger} | ${message} | ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AsyncTargetWrapper wrapper = new AsyncTargetWrapper();
|
|
|
|
|
wrapper.WrappedTarget = consoleTarget;
|
|
|
|
|
wrapper.QueueLimit = 5000;
|
|
|
|
|
wrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard;
|
|
|
|
|
|
|
|
|
|
config.AddTarget("console" , wrapper);
|
|
|
|
|
config.LoggingRules.Add(new LoggingRule("*" , NLog.LogLevel.Info , wrapper));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void InitLogger()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
NLog.LogManager.ThrowExceptions = true;
|
|
|
|
|
|
|
|
|
|
var config = new LoggingConfiguration();
|
|
|
|
|
|
|
|
|
|
nlog_to_console(config);
|
|
|
|
|
|
|
|
|
|
nlog_to_file_and_archive(config);
|
|
|
|
|
|
|
|
|
|
NLog.LogManager.Configuration = config;
|
|
|
|
|
|
|
|
|
|
CommonLoggingAdapter.AdapterLogger();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
ExceptionUtils.Current.Handle(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace NLog.RestTarget
|
|
|
|
|
{
|
|
|
|
|
[Target("QiNiuLogService")]
|
|
|
|
|
public sealed class QiNiuLogTarget : TargetWithLayout
|
|
|
|
|
{
|
|
|
|
|
public QiNiuLogTarget()
|
|
|
|
|
{
|
|
|
|
|
RepoName = string.Empty;
|
|
|
|
|
Authorization = string.Empty;
|
|
|
|
|
ServerAddress = string.Empty;
|
|
|
|
|
Parameters = new List<LogParameterInfo>();
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 队列名称
|
|
|
|
|
/// </summary>
|
|
|
|
|
[RequiredParameter]
|
|
|
|
|
public string RepoName { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 授权信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
[RequiredParameter]
|
|
|
|
|
public string Authorization { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 请求地址
|
|
|
|
|
/// </summary>
|
|
|
|
|
[RequiredParameter]
|
|
|
|
|
public string ServerAddress { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 参数
|
|
|
|
|
/// </summary>
|
|
|
|
|
[ArrayParameter(typeof(LogParameterInfo), "parameter")]
|
|
|
|
|
public IList<LogParameterInfo> Parameters { get; private set; }
|
|
|
|
|
|
|
|
|
|
private LayoutWithHeaderAndFooter LHF
|
|
|
|
|
{
|
|
|
|
|
get => (LayoutWithHeaderAndFooter)base.Layout;
|
|
|
|
|
set => base.Layout = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Layout Layout
|
|
|
|
|
{
|
|
|
|
|
get => LHF.Layout;
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value is LayoutWithHeaderAndFooter)
|
|
|
|
|
{
|
|
|
|
|
base.Layout = value;
|
|
|
|
|
}
|
|
|
|
|
else if (LHF == null)
|
|
|
|
|
{
|
|
|
|
|
LHF = new LayoutWithHeaderAndFooter()
|
|
|
|
|
{
|
|
|
|
|
Layout = value
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LHF.Layout = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void Write(AsyncLogEventInfo[] logEvents)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
bool isUploadToQiniu = Global.Instance.GlobalConfigBoolValue(ConfigConstant.LOG_UPLOAD_TO_QINIU, false);
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + isUploadToQiniu);
|
|
|
|
|
|
|
|
|
|
if (isUploadToQiniu)
|
|
|
|
|
{
|
|
|
|
|
SendTheMessageToRemoteHost(logEvents);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SendTheMessageToRemoteHost(AsyncLogEventInfo[] logEvents)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
foreach (var logEvent in logEvents)
|
|
|
|
|
{
|
|
|
|
|
var uri = new Uri(this.ServerAddress);
|
|
|
|
|
var eventInfo = logEvent.LogEvent;
|
|
|
|
|
|
|
|
|
|
//WiFi网络仅仅上传Error级别的消息
|
|
|
|
|
if (Global.Instance.IsWiFi)
|
|
|
|
|
{
|
|
|
|
|
if (eventInfo.Level != LogLevel.Error || eventInfo.Level != LogLevel.Fatal)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tenantNo = Global.Instance.Authc != null ? Global.Instance.Authc.TenantId : "000000";
|
|
|
|
|
var storeId = Global.Instance.Authc != null ? Global.Instance.Authc.StoreId : "000000000000000000";
|
|
|
|
|
var storeNo = Global.Instance.Authc != null ? Global.Instance.Authc.StoreNo : "000000";
|
|
|
|
|
var storeName = Global.Instance.Authc != null ? Global.Instance.Authc.StoreName : "";
|
|
|
|
|
var posId = Global.Instance.Authc != null ? Global.Instance.Authc.PosId : "000000000000000000";
|
|
|
|
|
var posNo = Global.Instance.Authc != null ? Global.Instance.Authc.PosNo : "000000";
|
|
|
|
|
|
|
|
|
|
Parameters.Clear();
|
|
|
|
|
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "appsign", Layout = Constant.APP_SIGN });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "version", Layout = Application.ProductVersion });
|
|
|
|
|
var theme = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_THEME, ThemeEnum.快餐.ToString());
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "layout", Layout = theme });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "tenantNo", Layout = tenantNo });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "storeId", Layout = storeId });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "storeNo", Layout = storeNo });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "storeName", Layout = storeName });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "posId", Layout = posId });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "posNo", Layout = posNo });
|
|
|
|
|
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "sequenceId", Layout = eventInfo.SequenceID });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "message", Layout = eventInfo.FormattedMessage });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "level", Layout = eventInfo.Level.Name });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "loggerName", Layout = eventInfo.LoggerName });
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "loggerDate", Layout = eventInfo.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss") });
|
|
|
|
|
|
|
|
|
|
string errorMessage = string.Empty;
|
|
|
|
|
var exception = eventInfo.Exception;
|
|
|
|
|
if (exception != null)
|
|
|
|
|
{
|
|
|
|
|
errorMessage = GetExceptionInfo(exception);
|
|
|
|
|
}
|
|
|
|
|
Parameters.Add(new LogParameterInfo() { Name = "exception", Layout = errorMessage });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
|
foreach (var item in Parameters)
|
|
|
|
|
{
|
|
|
|
|
stringBuilder.Append($"{item.Name}={item.Layout}\t");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string message = stringBuilder.ToString().Replace("\r", "\\r").Replace("\n", "\\n");
|
|
|
|
|
|
|
|
|
|
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
|
|
|
|
|
|
|
|
|
|
using (var client = new ExpectContinueAware())
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+Encoding.UTF8.GetByteCount(string.Concat(message, "\n")));
|
|
|
|
|
|
|
|
|
|
client.Headers.Add("Content-Type", "text/plain");
|
|
|
|
|
client.Headers.Add("Authorization", this.Authorization);
|
|
|
|
|
client.Encoding = Encoding.UTF8;
|
|
|
|
|
client.UploadStringCompleted += (sender, e) => { };
|
|
|
|
|
client.UploadStringAsync(uri, "POST", string.Concat(message,"\n"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
ExceptionUtils.Current.Handle(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetExceptionInfo(Exception e)
|
|
|
|
|
{
|
|
|
|
|
string errorMessage = e.Message + " " + e.StackTrace;
|
|
|
|
|
if (e.InnerException != null)
|
|
|
|
|
{
|
|
|
|
|
errorMessage += e.InnerException.Message + " " + e.InnerException.StackTrace;
|
|
|
|
|
if (e.InnerException.InnerException != null)
|
|
|
|
|
{
|
|
|
|
|
errorMessage += e.InnerException.InnerException.Message + " " + e.InnerException.InnerException.StackTrace;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return errorMessage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ExpectContinueAware : System.Net.WebClient
|
|
|
|
|
{
|
|
|
|
|
protected override System.Net.WebRequest GetWebRequest(Uri address)
|
|
|
|
|
{
|
|
|
|
|
System.Net.WebRequest request = base.GetWebRequest(address);
|
|
|
|
|
if (request is System.Net.HttpWebRequest)
|
|
|
|
|
{
|
|
|
|
|
var hwr = request as System.Net.HttpWebRequest;
|
|
|
|
|
hwr.ServicePoint.Expect100Continue = false;
|
|
|
|
|
}
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Auth
|
|
|
|
|
{
|
|
|
|
|
public const string ContentType = "Content-Type";
|
|
|
|
|
public const string ContentMD5 = "Content-MD5";
|
|
|
|
|
public const string Date = "Date";
|
|
|
|
|
public const string Authorization = "Authorization";
|
|
|
|
|
|
|
|
|
|
private string accessKey;
|
|
|
|
|
private string secretKey;
|
|
|
|
|
|
|
|
|
|
public Auth(string accessKey, string secretKey)
|
|
|
|
|
{
|
|
|
|
|
this.accessKey = accessKey;
|
|
|
|
|
this.secretKey = secretKey;
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// SignRequest signs the request in the algorithm of Pandora
|
|
|
|
|
/// see doc here at
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url"></param>
|
|
|
|
|
/// <param name="method"></param>
|
|
|
|
|
/// <param name="headers">
|
|
|
|
|
/// method - request method
|
|
|
|
|
/// resource - request path
|
|
|
|
|
/// expires - deadline in unix timestamp in seconds
|
|
|
|
|
/// contentType - content type
|
|
|
|
|
/// contentMD5 - content md5, optional
|
|
|
|
|
/// headers - sorted headers whose keys start with X-Qiniu-
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="qiniuSubResource"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public string SignRequest(string url, string method, Dictionary<string, string> headers)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, object> tokenDesc = new Dictionary<string, object>();
|
|
|
|
|
//filter the qiniu heades
|
|
|
|
|
List<string> qiniuHeaderKeys = new List<string>();
|
|
|
|
|
foreach (string key in headers.Keys)
|
|
|
|
|
{
|
|
|
|
|
if (key.StartsWith("X-Qiniu-"))
|
|
|
|
|
{
|
|
|
|
|
qiniuHeaderKeys.Add(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qiniuHeaderKeys.Sort();
|
|
|
|
|
string canHeadersStr = null;
|
|
|
|
|
if (qiniuHeaderKeys.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
List<string> canHeaders = new List<string>();
|
|
|
|
|
foreach (string key in qiniuHeaderKeys)
|
|
|
|
|
{
|
|
|
|
|
canHeaders.Add(String.Format("{0}:{1}", key.ToLower(), headers[key]));
|
|
|
|
|
}
|
|
|
|
|
canHeadersStr = String.Join("\n", canHeaders);
|
|
|
|
|
}
|
|
|
|
|
if (!String.IsNullOrEmpty(canHeadersStr))
|
|
|
|
|
{
|
|
|
|
|
tokenDesc["headers"] = canHeadersStr;
|
|
|
|
|
}
|
|
|
|
|
//check other headers
|
|
|
|
|
if (headers.ContainsKey(ContentType))
|
|
|
|
|
{
|
|
|
|
|
tokenDesc["contentType"] = headers[ContentType];
|
|
|
|
|
}
|
|
|
|
|
if (headers.ContainsKey(ContentMD5))
|
|
|
|
|
{
|
|
|
|
|
tokenDesc["contentMD5"] = headers[ContentMD5];
|
|
|
|
|
}
|
|
|
|
|
Uri reqURI = new Uri(url);
|
|
|
|
|
tokenDesc["resource"] = reqURI.AbsolutePath;
|
|
|
|
|
tokenDesc["method"] = method;
|
|
|
|
|
tokenDesc["expires"] = GetUnixTimestampInSeconds() + 24 * 60 * 60 * 7;
|
|
|
|
|
string tokenDescData = JsonConvert.SerializeObject(tokenDesc);
|
|
|
|
|
return String.Format("Pandora {0}", this.SignWithData(tokenDescData));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string encodedSign(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(this.secretKey));
|
|
|
|
|
byte[] digest = hmac.ComputeHash(data);
|
|
|
|
|
return UrlSafeBase64Encode(digest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string encodedSign(string str)
|
|
|
|
|
{
|
|
|
|
|
byte[] data = Encoding.UTF8.GetBytes(str);
|
|
|
|
|
return encodedSign(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Sign(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
return string.Format("{0}:{1}", this.accessKey, encodedSign(data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Sign(string str)
|
|
|
|
|
{
|
|
|
|
|
byte[] data = Encoding.UTF8.GetBytes(str);
|
|
|
|
|
return Sign(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string SignWithData(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
string encodedData = UrlSafeBase64Encode(data);
|
|
|
|
|
return string.Format("{0}:{1}:{2}", this.accessKey, encodedSign(encodedData), encodedData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string SignWithData(string str)
|
|
|
|
|
{
|
|
|
|
|
byte[] data = Encoding.UTF8.GetBytes(str);
|
|
|
|
|
return SignWithData(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static long GetUnixTimestampInSeconds()
|
|
|
|
|
{
|
|
|
|
|
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // current zone
|
|
|
|
|
long timeStamp = (long)(DateTime.Now - startTime).TotalMilliseconds / 1000; // timestamp in seconds
|
|
|
|
|
return timeStamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string CreateHttpDateTime(DateTime dateTime)
|
|
|
|
|
{
|
|
|
|
|
return dateTime.ToUniversalTime().ToString("r");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string CreateRFC3339DateTime(DateTime dateTime)
|
|
|
|
|
{
|
|
|
|
|
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static DateTime FromRFC3339DateTime(string dateTimeStr)
|
|
|
|
|
{
|
|
|
|
|
return DateTime.Parse(dateTimeStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string UrlSafeBase64Encode(string text)
|
|
|
|
|
{
|
|
|
|
|
return UrlSafeBase64Encode(Encoding.UTF8.GetBytes(text));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string UrlSafeBase64Encode(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string UrlSafeBase64Encode(string bucket, string key)
|
|
|
|
|
{
|
|
|
|
|
return UrlSafeBase64Encode(bucket + ":" + key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] UrlsafeBase64Decode(string text)
|
|
|
|
|
{
|
|
|
|
|
return Convert.FromBase64String(text.Replace('-', '+').Replace('_', '/'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class LogParameterInfo
|
|
|
|
|
{
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
|
|
|
|
|
public object Layout { get; set; }
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
|
|
|
|
var o = obj as LogParameterInfo;
|
|
|
|
|
return o != null && Name == o.Name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
return 31 * 17 + ((this == null) ? -1 : base.GetHashCode());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|