From 6b50f5cb3e3289cc81789dfca716dcda2b600a76 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 31 Oct 2014 15:41:53 +0800 Subject: [PATCH 01/21] =?UTF-8?q?=E5=A2=9E=E5=8A=A0httpmodule=E7=9A=84?= =?UTF-8?q?=E4=B8=B2=E8=81=94=E5=B7=A5=E4=BD=9C=20=E6=B7=BB=E5=8A=A0cathel?= =?UTF-8?q?per=20=E6=B7=BB=E5=8A=A0=E4=B8=A4=E4=B8=AA=E5=B0=8F=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 184 +++++++++++++++++ Cat.Net.csproj | 192 +++++++++--------- Cat.cs | 20 +- Message/Spi/IMessageManager.cs | 11 +- Message/Spi/IMessageProducer.cs | 8 +- .../Spi/Internals/DefaultMessageProducer.cs | 4 + Properties/AssemblyInfo.cs | 9 +- Util/AppEnv.cs | 50 +++++ Util/CatHelper.cs | 168 +++++++++++++++ Web/CatHttpHandler.cs | 32 +++ Web/CatHttpModule.cs | 72 +++++++ 11 files changed, 643 insertions(+), 107 deletions(-) create mode 100644 .gitignore create mode 100644 Util/AppEnv.cs create mode 100644 Util/CatHelper.cs create mode 100644 Web/CatHttpHandler.cs create mode 100644 Web/CatHttpModule.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24d319c --- /dev/null +++ b/.gitignore @@ -0,0 +1,184 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +*.log +*.orig + + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +#!packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ \ No newline at end of file diff --git a/Cat.Net.csproj b/Cat.Net.csproj index a8b32cd..96659a5 100644 --- a/Cat.Net.csproj +++ b/Cat.Net.csproj @@ -1,94 +1,100 @@ - - - - Debug - x86 - 10.0.0 - 2.0 - {7B5F7994-F1D1-494D-85B9-935A478591AD} - Library - Com.Dianping.Cat - Cat - 0.1.1 - false - Cat .Net Client - v4.0 - - - - true - full - false - bin\Debug\ - DEBUG; - prompt - 4 - AnyCPU - true - - - none - true - bin\Release\ - prompt - 4 - AnyCPU - true - - - - - - - - - - Code - - - - - - - - - - Code - - - - Code - - - - - Code - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - + + + + Debug + x86 + 10.0.0 + 2.0 + {7B5F7994-F1D1-494D-85B9-935A478591AD} + Library + Com.Dianping.Cat + Cat + 0.1.1 + false + Cat .Net Client + v4.0 + + + + true + full + false + bin\Debug\ + DEBUG; + prompt + 4 + AnyCPU + true + + + none + true + bin\Release\ + prompt + 4 + AnyCPU + true + + + + + + + + + + + + Code + + + + + + + + + + + + Code + + + + Code + + + + + Code + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cat.cs b/Cat.cs index dbd6510..18619f3 100644 --- a/Cat.cs +++ b/Cat.cs @@ -1,11 +1,12 @@ -using Com.Dianping.Cat.Configuration; -using Com.Dianping.Cat.Message.Spi; -using System.Collections.Generic; -using System.IO; +using Com.Dianping.Cat.Configuration; +using Com.Dianping.Cat.Message.Spi; +using Com.Dianping.Cat.Message.Spi.Internals; +using Com.Dianping.Cat.Util; +using Microsoft.Web.Infrastructure.DynamicModuleHelper; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Xml; -using System.Linq; -using Com.Dianping.Cat.Message.Spi.Internals; -using Com.Dianping.Cat.Util; namespace Com.Dianping.Cat { @@ -57,6 +58,11 @@ public static void Initialize(string configFile) public static bool IsInitialized() { return Instance._mInitialized; + } + + public static void RegisterHttpModule() + { + DynamicModuleUtility.RegisterModule(typeof(Com.Dianping.Cat.Web.CatHttpModule)); } private static ClientConfig LoadClientConfig(string configFile) diff --git a/Message/Spi/IMessageManager.cs b/Message/Spi/IMessageManager.cs index 4973ddb..70a4d1d 100644 --- a/Message/Spi/IMessageManager.cs +++ b/Message/Spi/IMessageManager.cs @@ -1,4 +1,5 @@ -using Com.Dianping.Cat.Configuration; +using Com.Dianping.Cat.Configuration; +using Com.Dianping.Cat.Message.Spi.Internals; namespace Com.Dianping.Cat.Message.Spi { @@ -72,6 +73,12 @@ public interface IMessageManager /// Be triggered when a transaction ends, whatever it's the root transaction or nested transaction. However, if it's the root transaction then it will be flushed to back-end CAT server asynchronously. /// /// - void End(ITransaction transaction); + void End(ITransaction transaction); + + /// + /// get MessageIdFactory + /// + /// + MessageIdFactory GetMessageIdFactory(); } } \ No newline at end of file diff --git a/Message/Spi/IMessageProducer.cs b/Message/Spi/IMessageProducer.cs index bdb51ea..3fc9259 100644 --- a/Message/Spi/IMessageProducer.cs +++ b/Message/Spi/IMessageProducer.cs @@ -126,6 +126,12 @@ public interface IMessageProducer /// /// transaction type /// transaction name - ITransaction NewTransaction(String type, String name); + ITransaction NewTransaction(String type, String name); + + /// + /// Create MessageId + /// + /// + String CreateMessageId(); } } \ No newline at end of file diff --git a/Message/Spi/Internals/DefaultMessageProducer.cs b/Message/Spi/Internals/DefaultMessageProducer.cs index cf3afcb..a5722a5 100644 --- a/Message/Spi/Internals/DefaultMessageProducer.cs +++ b/Message/Spi/Internals/DefaultMessageProducer.cs @@ -11,6 +11,10 @@ public class DefaultMessageProducer : IMessageProducer public DefaultMessageProducer(IMessageManager manager) { _mManager = manager; + } + public String CreateMessageId() + { + return _mManager.GetMessageIdFactory().GetNextId(); } #region IMessageProducer Members diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index abb13d7..4c3509b 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Reflection; +using System.Web; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. @@ -10,13 +11,13 @@ [assembly: AssemblyProduct("Cat")] [assembly: AssemblyCopyright("Copyright © Dianping.com 2013")] [assembly: AssemblyTrademark("Dianping.com")] -[assembly: AssemblyCulture("")] - +[assembly: AssemblyCulture("")] +[assembly: PreApplicationStartMethod(typeof(Com.Dianping.Cat.Cat), "RegisterHttpModule")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion("0.1.0")] +[assembly: AssemblyVersion("0.3.1.0")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. diff --git a/Util/AppEnv.cs b/Util/AppEnv.cs new file mode 100644 index 0000000..a3f277c --- /dev/null +++ b/Util/AppEnv.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Web; + +namespace Com.Dianping.Cat.Util +{ + public static class AppEnv + { + + static AppEnv() + { + IP = getLocalIP(); + } + + public static string IP { get; private set; } + + private static string getLocalIP() + { + try + { + return NetworkInterfaceManager.GetLocalHostAddress(); + } + catch + { + return string.Empty; + } + } + + public static string GetClientIp(HttpRequest request) + { + if (request == null) + { + return string.Empty; + } + var serverVariables = request.ServerVariables["HTTP_X_FORWARDED_FOR"]; + if (!string.IsNullOrEmpty(serverVariables)) + { + var items = serverVariables.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (items.Length > 0) + { + return items[0]; + } + } + return request.ServerVariables["REMOTE_ADDR"]; + } + } +} diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs new file mode 100644 index 0000000..4bf1231 --- /dev/null +++ b/Util/CatHelper.cs @@ -0,0 +1,168 @@ +using Com.Dianping.Cat.Message; +using Com.Dianping.Cat.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Web; + +namespace Com.Dianping.Cat.Util +{ + public class CatHelper + { + public const string CatRootId = "X-Cat-RootId"; + public const string CatParentId = "X-Cat-ParentId"; + public const string CatId = "X-Cat-Id"; + + public static ITransaction BeginClientTransaction(string name, string requestingUrl, WebRequest request) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return null; + try + { + var tran = Cat.GetProducer().NewTransaction("HttpCall", name); + tran.Status = "0"; + Cat.GetProducer().LogEvent("HttpCall.Server", "HttpRequest", "0", requestingUrl); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + string serverMessageId = Cat.GetProducer().CreateMessageId(); + string rootMessageId = (tree.RootMessageId ?? tree.MessageId); + string currentMessageId = tree.MessageId; + Cat.GetProducer().LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); + + if (request != null) + { + request.Headers.Add(CatHelper.CatRootId, rootMessageId); + request.Headers.Add(CatHelper.CatParentId, currentMessageId); + request.Headers.Add(CatHelper.CatId, serverMessageId); + } + + return tran; + } + catch + { + return null; + } + } + + public static ITransaction BeginServerTransaction(string url, string name) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return null; + try + { + var httpContext = System.Web.HttpContext.Current; + if (httpContext == null) { return null; } + var request = httpContext.Request; + string rootMessageId = request.Headers[CatHelper.CatRootId]; + string serverMessageId = request.Headers[CatHelper.CatParentId]; + string currentMessageId = request.Headers[CatHelper.CatId]; + + var tran = Cat.GetProducer().NewTransaction("HttpService", url); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + tree.RootMessageId = rootMessageId; + tree.ParentMessageId = serverMessageId; + if (!string.IsNullOrEmpty(currentMessageId)) + { + tree.MessageId = currentMessageId; + } + tran.Status = "0"; + Cat.GetProducer().LogEvent("HttpService.Server", name, "0", AppEnv.GetClientIp(request)); + return tran; + } + catch + { + return null; + } + } + + public static ITransaction BeginTransaction(string type, string name) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return null; + try + { + var tran = Cat.GetProducer().NewTransaction(type, name); + tran.Status = "0"; + return tran; + } + catch + { + return null; + } + } + + public static void AddServerEvent(string type, string name) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return; + Cat.GetProducer().LogEvent(type, name, "0", null); + } + + public static string GetRootMessageId() + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return string.Empty; + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + + string rootId = tree.RootMessageId; + if (string.IsNullOrEmpty(rootId)) + { + rootId = tree.MessageId; + } + return rootId; + } + + public static string GetMessageId() + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return string.Empty; + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + return tree.MessageId; + } + + public static void SetTrancationStatus(ITransaction tran, string status) + { + if (tran != null) + { + tran.Status = status; + } + } + + public static void SetTrancationStatus(ITransaction tran, Exception exception) + { + if (tran != null) + { + tran.SetStatus(exception); + } + } + + public static void CompleteTrancation(ITransaction tran) + { + if (tran != null) + { + tran.Complete(); + } + } + } +} diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs new file mode 100644 index 0000000..84781ef --- /dev/null +++ b/Web/CatHttpHandler.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using System.Web.SessionState; +using Com.Dianping.Cat.Util; + +namespace Com.Dianping.Cat.Web +{ + public class CatHttpHandler : IHttpHandler, IRequiresSessionState + { + public IHttpHandler Handler { get; set; } + public bool IsReusable { get { return Handler.IsReusable; } } + + public void ProcessRequest(HttpContext context) + { + var tran = CatHelper.BeginServerTransaction(context.Request.Url.ToString(), Handler.GetType().FullName); + try + { + Handler.ProcessRequest(context); + } + catch (Exception ex) + { + CatHelper.SetTrancationStatus(tran, ex); + CatHelper.CompleteTrancation(tran); + throw ex; + } + CatHelper.CompleteTrancation(tran); + } + } +} diff --git a/Web/CatHttpModule.cs b/Web/CatHttpModule.cs new file mode 100644 index 0000000..7ebd7ac --- /dev/null +++ b/Web/CatHttpModule.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using Com.Dianping.Cat.Message; +using Com.Dianping.Cat.Util; +using System.IO; + +namespace Com.Dianping.Cat.Web +{ + public class CatHttpModule : IHttpModule + { + + public void Init(HttpApplication context) + { + context.PostMapRequestHandler += context_PostMapRequestHandler; + } + + + + void context_PostMapRequestHandler(object sender, EventArgs e) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + { + return; + } + try + { + var context = System.Web.HttpContext.Current; + var request = System.Web.HttpContext.Current.Request; + + if (filter_Handler(context))//如果被过滤了,就直接跳过 + return; + + var handler = new CatHttpHandler { Handler = context.Handler }; + context.Handler = handler; + } + catch (Exception) + { + + } + } + + + + bool filter_Handler(HttpContext context) + { + var request = context.Request; + var handlerType = context.Handler.GetType(); + var attibutes = handlerType.GetCustomAttributes(typeof(Attribute), false); + + if (context == null) + return true; + + if (context.Handler.GetType().FullName.Equals("System.Web.Handlers.TransferRequestHandler", StringComparison.OrdinalIgnoreCase)) + return true; + + if (context.Handler == null && File.Exists(request.PhysicalPath)) + return true; + + if (attibutes.Any(p => p.GetType().FullName == "TongCheng.SOA.Interface.Attributes.SOABusiness")) + return true; + + return false; + } + + public void Dispose() + { + } + } +} From d46b6be46eb0b50e1d7716cb0a69b650b374681f Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 31 Oct 2014 15:57:40 +0800 Subject: [PATCH 02/21] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cat.Net.csproj.user | 6 ------ bin/Debug/Cat.dll | Bin 43520 -> 0 bytes bin/Debug/Cat.pdb | Bin 130560 -> 0 bytes .../Debug/Cat.Net.csproj.FileListAbsolute.txt | 5 ----- obj/x86/Debug/Cat.dll | Bin 43520 -> 0 bytes obj/x86/Debug/Cat.pdb | Bin 130560 -> 0 bytes ...ignTimeResolveAssemblyReferencesInput.cache | Bin 5412 -> 0 bytes 7 files changed, 11 deletions(-) delete mode 100644 Cat.Net.csproj.user delete mode 100644 bin/Debug/Cat.dll delete mode 100644 bin/Debug/Cat.pdb delete mode 100644 obj/x86/Debug/Cat.Net.csproj.FileListAbsolute.txt delete mode 100644 obj/x86/Debug/Cat.dll delete mode 100644 obj/x86/Debug/Cat.pdb delete mode 100644 obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache diff --git a/Cat.Net.csproj.user b/Cat.Net.csproj.user deleted file mode 100644 index 55f44b9..0000000 --- a/Cat.Net.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - ShowAllFiles - - \ No newline at end of file diff --git a/bin/Debug/Cat.dll b/bin/Debug/Cat.dll deleted file mode 100644 index e871f0603ae9ddd5304490fc61fa6a38560b6c91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43520 zcmeIbdwf*Y)i=J*IWy1`?TpMBBQ~M8Dmeo)-=WraP?Hw-c?_ z47%plH5=U4ULd_WsQHMF0b?rc^T+W_;P(amvUCYNbR{=(B0oPVp$VR|1}(dhs6zgi zo({+a___T!(WRVuf#_j2#DpJ>Lcq;APSh}=>@HP)3KR${i zfnU}Y`jr19h_=tm<#Rniifk_+!yWZr{FePJgF)uy(pejXEGv;?gZl+2Cd-JH7Xc@8 z{J6@nY&v=A3s({KrG$e+@y|Uj)00u60GQ=tc#c3456vd3oUc(3tN= D8F&_d?y zGjwBwf!USw4Vor0P?Yoo+_N+0PMN9a(S7EO;0PlusWOvLhe6a)eFFwuhno_LHxy+s zMqn|c5ozI!m%M>Y1Yp#*t?)!vT2Nt zEHeaz%_ATYRoLbXKxUm1B^y9A&z!J+fS-01I
  • YebVp{mbmd?M;Sf=>#u2F(i8H zRO!e;dJ^4iCSFFT?Y=@tS3(a3Z&AWe(m$-G)1E;Kz{{~A%UX3(i=Wg2=G+P}%2%`3 zF`8PYz{s34>^P7!oqpG8`zh*!K3`L+KEp9ZEl}~3da(`R*)-Fx6Q<=&G%XdR$FAq1 z*|P~QH;3&%yXbF@+~^ipADtkb(kF+$8`KZ5+3cxcQL-e{%NUspT6jm4@>wtrB)9|f z^%|K$Q@89P;AUF(48UYbABI}UH~SpDsw#=Dgd@;c0CK5u2UlaCoPu`LxI?fDVB9f6 zjXO1FKqvl0@!cX_z|rkgc*v@Bt0|I;Ef^0cLYouuVxzd=$4PE@%G4+W-mDh^y?XZu zB9BM+jv#dKpkE^riyw~B6~k$)u8b(@YS3}W2*+c)Fvs}rlb%X%)H^rgASykJ?ga@f zp&#J7Jb3tN!_zG#0p-rbXvno7$+rO=v~rgqZvzYG4wyX1Ow!HdT0Cb`%OX_g0O%M%Ie|0& z^aJb*Fg+M^R1nXw6D$cv1jj*0rTonji`sQ<(8p?JxcF-%3TjNYi@(ZwXi>~AM+>Q#+tTVnfpBo z;!==&E2HvXMjFtQm*EjKE&DhlMG!OpG2}BFR= z_?Y?IhS^L()71GM+Jg$P17Z>~EKf5G%U*0o&BZ2gl(W&AQmN94T2Lx#IVzk1+MuO# zw+0ZirsEOv@gU*KCs3D6U+$f#ZqGnb@^Zj7W@VFnxd)2aZXm+szYd|=Gqu3N>j;(A??MG_Rb~^2G{8!+G1*OJoZHo_27x2hH*3}qT84$xD)Un&Kvg6^^>fn zLM^R&+@6QR0(ffyK?W@|cRkvZ)kktOw+E-jFf(&E0$r2b!YKGFP%@)Pzn=knBCMd_ zkp==~t@32qqSaIeqJhPav3TW8Vj#aAOr<|V2wrESa%He(cQnYMEl>cgp)*CiN>9|2 zdk;&-<`Bsc8$G!P0EuyuS3oG3-WxT=PL(h^%FCoiWHQl5m6jQ`Y=kQKE?SZvgLrdq zdA>0k9D+Tx%1|`Kj>lMEG*s+5(>@2alqscf7Ue#I_MMTaI&ger{28P_D+HK~okKB`(b4BJjWfS3KY$XHUul1=VE6wB&ip3V=Ou0t^|CxWc^r23UBM z!{6-E$~%7m$Pq+;kDkAdv(TD10vzvoy@Ox&8!S`;x?$P_+(}T<(a#`^T_+x6W*_8I z!)^q1aUm9ju%2X&OH?fO(8Z{Oh!>@Jz!SIOonXmCc@T964Etwz!qS+TVq74wCpjdg z2*0L1ldC8hIcyJU;u!5QIE_`z3iIEWNBUUi%L!W7_|vGM4>kmGRlXR2jeh zipm7+->6K`{vBs{DUqAS4dyOIntUDlJAXcsYsBX~6f%gg=q=g=b2$w5_Y7h+wM!JL zj7Q^h_me;Bk?Fj0f_bin5E!Md_A{ei{Q!H7&mKVuui-I{G0u5iD>=(X55W#RXqkD8 zJW;O=vCWyD%IZZ|VUrm3BC`n@t7+AEDcV6;IiU|kic%lUjQS>#!WZ>BQuv~QNu=^wX)1xBjR?V@{}?OLK7v3hN59d#K$OPh zVx*N7Ag{YrUG}Yoayr+?(L>$D%bg)~yKknA~e>b01NtIs;I0 z3FFE=-Sv2YVPTbTfzuwcpJf_K%P~RoPEY zQ})x-l>PoRWq&+P*`H2R_KVY${pvJjzdlXbZ^xDeT)U{XOXYl%__PRteXKpb&p3qM zrmk}%M%jjp7c;!Rn@fiDA}zzo$@<-}qbLYRJhhW+w#?NGYcOma!7AoSVucV$Vpv9Pf13u-p%?L2Ly1C>cXo=DOQo>=EWx@&w=19#0ln zAal{TL8-bVz_U27%@^OuTARFr#P(3O-Oe)2VSrX!Yh4C!2W>p0$ihAh#HK=xly!&V z*eOpV3lk}C#Vfr&J?eE<1|jUAYr1(I>c{q<`F=<0b}7EyAz!80veX#DGP7KIK8x}+ zt#YMrnVS0!>}J^9$+@s+%=8*HdCj5A`UUO77C8RF%o@OIhj)dD174~MwM;i}+w`9u8^*orrJ^Srn+;$(iUe| z;7~fdVgS6$%W?8hDraC^Iix6+Gcf!0q|ZnmMpxGC?oQrg_^!v)UZF@KF^cY)xTzc%-2LDX6HCPCCoFvS@ei|mN;Ja&Y<-{u|U`HN_I z1R2-l9A^=Od4CD}U4}~VIdLM`QJsOp#uj1N6Z|rY1CGQan3-y7;_)~i(gX zQqS?h&3rf^g=nDm zNTtT(ED+)sHrWbB_LWF*s(lq6>bT3^hHTJ!AHwfcHOnYpL_e$edWw3+tz5(2j@rCU zm$-|0>9lq1lu+-`6HuS>I{=NaByI$8$1MsWZx2e;StR!@`Wn5C@ox75UyFMojw0-r z9=aZ7$fC}BV4q}~D`NGq6VL0QyE-}i0E+UvfEr~KP7e8z)ZyoLAF$yTZ2dCG1l5JN;b=PiUKSZu!uzN%X1QSa8mcPJJYGxAa!P%b?_Qwv z<*s%1Ksqfh*`8ek7KBIQR8btou)oVfD(_H5e9O(gV%Rr|{2bSKeY6^Uzzb`jO-eWf ziwj)gl%s4X8;#P$fu?}WO6JZ3zc$kN=IZirtAN*~PDYI}e*JQaJI)M~(_=WtA#WN+d zi9${XR4Ij&)v6q=ETvUVsAWn#)Dq0K7ujR&%Rbl)q5b3Z;df_5OtwIaR;8kj@>xt> z?)xR>nqa^tFmi2-2kzuz?Evfx*XMUant|soE4RMe-#-`At9s5&ir8E2uPf^A_GL|=wRV8Ms+=swU zUfruvn}0MB&wUTbW4I+h3>y0uByh(!vR;<`COks%-QkukxQ@RS0PAJnhK$s*-;A97 z79_5&2*o&$nv6o$l*!vci({kbt(k+@$qK%adCNU^~?{OC7+{m*%1_&^vdRG8mc zdu8dYw~>YU2x~7d^94X-4l$aIsCEtZC#r&~A@?OaWW1-}`IhV_$T7$X{tBcmfqdw;ym%5s$()L)i zt~MHQv2{_uo2`$A-0ak7(9O!(6K)!F-vMI`i=$}8?TTMB$~y*S_<>Rf4P|CEapfre zd)MZ!T8nn^U7x=MIryVMi}pwlEf*;*bn46B521Vyd+iox;n!Kn8c9_{Xc0 zz|6@dn&nDV;o5AxMDRjfuZjVZ-vof`(A*j}R8(r@`jO{-qGQuYGWMvHZ zpdUacTUlx5?&Eru%HhV4e5N25ZAO^1+nZ3MyXhZHm zARcTvi*zgbpWrBOL1O&{GT+EDZpa$JEa+(o6Hd2q-!I2FPwOYmAA-7UhX>3HV==ia zVczmRU+$@K2&^Lv`WWO|!E(j1nPSOj1aZO)?O+>)5LHBfx&Fg4mz7{rgL@2gEjw^2 zasb-l!f1pa`T@3`+EehRgdxMmDi2e+E`&|JS7MW-&MnTQ+=U??nq0Ni!{x8qLCEJT zoZX0JxrcePf#!1?9iRF+`W|^L7w1>%9XCyE9hcrio6Ep@4&LwOr~QcZ4Zp02{S+^r z1^W@vrFd!t=FFQv(R23s?wOEI$AM{+bqVNM8+H%TV=-0&j9A7^VP(rtX$nhk80~dV zk+MoO_X((6t|IpNV;w^}9^KM%E-%K}JA#&~jEfb;egr+oF2YUWKvX;huesH92e4IU z=%fcNAE(6O%96u}7OliZ9-obO!AR|*%Sz-*y6{SHF+NYgTjj|Ws*%I)Xzowrlqheg z%zNh4I%b*J1H$MXRebKN+^_8L7j4T}`Y3t^my2P%1_pQKbHjiDSU}3Eb?!@e16SS; z;3Ie+$PgyfX)o80+aG7pJrPzk#S>vR?u+c1B?hLmtD)zcu=PDJJmxiKAHM{Qg&|z| zdA3k^jCB!DJc}MV`Pyq*lKt}iH0=u#8j!z<=A+>V-PVz@vTCGbyuXawV&;}$ledqiy;uSlEN z+bsi@}z4d_lSm6ZG8IQg)7R%2!gHP2ULhW!lhaMC48 zzoLs9e4xrNy`p3IU0!~7fL~DfEm-G2g`%KUV}BY?vJzjni0sdB-jvs}L#Div9nOU# zW8T{4uBmYssm{CRI5YO!j|m@l+)Hb-DYxP8*S4^c*^#^Lw1kc?oQIdnbKe7HomyI9 z;};(*P%U;(U65pq13_oFarr)|K)au(^Vm0d-;9-f`FpeYq{CleF#E64b$aq|44ia; z*BHPo1Ygz<>d?%%RvnOuP-lWK*~c-`*2j&ym($y z4LsZr`+7OKN5J><^$XMRQ4h{0)L9S9hPrI1zgL%OCacSK{d;wprdYRRvrFBoY%>Om zIxCy4E-zzwNG<~+ z{{_Y(?7;;%p4|jF;oxe$$yD}#fFOuZ%BV{A34kGAwR%;F=V~uihg-UQ&ln{X?t!HR z%l;y$@rGl|LSFQ^ESRIi?FnRIV&-%2m^$%r3;AbXY&_1cEkgdq$`(LIGP!PA+ku zvBiZQBKu3IK(jZx=;T=(h`%hzQe6eFbcsA(`zu1h7;ZMcJ8nM(%*@$ubZO#zBf~R3 zx9%xtE4slPgrE|odU&NTNfi^m~^$IcN^Cs-|4-?Ytmz8#t6``HDH-^#JUdo zhmeoZk9#CS$p`>dh9j)3~Wzs`ccUN6c$E(-^U#sGFe=TXa znq{l1W=v}}x42F)Z%Oe^>{{5Tb_D5N zl76Ok2&>FzkvGX($DF(C&PIMu9m_K+n8S6*(q!D_9VBSA95R6c;flIeEF#atK z+?!7zZ_@WA{YeAMV>a%F4=bb%W<)bEy4i~yeAP-gnT-Gj-h4FMB>aCW~IQTI< z529=Up6kU8PNS6FCuLLcmI&9oA7!=pWa~UBd&DU_TgpD`lr567uSr=2P8_)2_od8* zUUr~1Df^=XwF_iw+(HF*NnEc^Yt-;^G;I)`^R$aF?&|0&fv(Up0%e5qRzO~qWeLkw zI)-0AzHwq7^sS%=0D18~s)9Z)(3yCT@jmGOxbQp^Z;x?%-_(8uE@#qVfvWVWx)<*f zzEz+F0-Z&73AEgS-YL-K0ww9)0`&D)~BVYF%VK_iCr561bp#cTAeMtX*49WWa_ z=Oeua7>%w&zM1BFxpW~?jV|;JT-QDcBxIbYIFr10y&ue2yVfqXNa6%6Ry%Agsg1$Bei zD}B&lEglJ;1=smhkn<;kOvzF;)5_2$;Ma$)Lb@%q18FA2GVc#D>FX14 z6}qYRqljMUI|cbZWz++@-hoaS(*PZIpl6ITF`GT&K+hRTK%a4-myLNipZlhQz;h#@ zKRac!Jl*Wo&4izueDgh90VN&i0#7}liyf%lGY!xx2Re*;m=hf6EuKC=*|K^T9V)A5 z(VG<{8u_>}{G15i4K5|<*cj-8W1x?tg%aiCo_@Z|mY);huX*;CLC>L{gk5gI`x5Gv zL;LtnTh;Tv;)(OO2sqH6plt~A(%7rMy-H`m82zQwEvieqK-R86sf+6`?~Yd$bHn(1{}Sam;eR6iDeX zgKB4})>(%c^kWBN9cIvP9Ef$8NmFJjO4eZ}B^-!#IE&7AAlBh5TBab;AxWbGDII2! z=Pco(u@1B7Y=I8SXq-h$1iF*vT3J%{szWda?c zE3Fp+J;94`Izl&EKgF!NXtsjhZM}lGi}$xE=o8kfc>ngJ4)mm@Y4hpN4)m<$*UqJ+ z!}IFuSB%%u@;Oqk_QCk~FuSRr6X9Q2QLP01)ryxv6~3uuP>Zjg-xHIc6W+zX=2DsW zBHvlsqGH)PUz}v)0?Y~TCe$lI+sBmc^v%^uWm!Ori(HQRR%ke2m7f#dW4_fI4tgEv z0pDfXMYL8lQr@$S1_e5yalK`9M-f`BwbBy~#PwEC)m&zCBYhh@+bKCuLBH_r(=MT9 z3l#KUD7%#2>p&j=E!rCT18?zAFY14rwvMh{$k0vH;D5K)Nr(CP36SmothR~1=|BZQ zm(#PaQ)Soszo>1dKNq2|X;;u9G3ib82LIQzE9ojRqS9y^6%<5o@_$F$Mn@g!t^Q}V z?eugRWs2G3=S27}e?OI=_xbmhL64wphav>!<0vaZUq@L_vF!UOD?;8M`(M<0-7@bh zfYL?iFaDRco%F28@{*rh-$lQ7AZ~pZ{ndfE^**YU&VI?yt@lxb199saI@f`?^$aah zkjAa=rgaX)t?!{74#cfzX-FVtvwq_D?s!zaG4NZhpKf!Yw*a!~T@G{ypuP04Ku72u zfj?_`S}%@wC-KJxWIsUk-Rc=|VpO zw4bt0y}zN}F#UH~yFVGaC|hz7LrTtT=qo~bgpSo_%xmb7P#&SFbs6&jwUtp`OIroH z(Z_S@wUk$7zUPcSKzygnE1ho#`)Gs?D~Ns^?8a`VaIuuZo_&DS&k3&{nxkJ|go2@S z^wA8)ILLgN;0 zr|k~JE!;u(I1smR7yZ?NxP^Dn*5#@mw{Q=AT|s`1k@wQe4#Y9?UaD9jlzxtp_tIho zX&m|PrPU6^QSn~#v?-n(2j5Le1^GFezMHl?WgJc4P4_qu$H=4fT?gVAd6bs4D=r)( z@1t7Cf!LlW=w%0Dd!C@sW+e;T^NTdyf!Lm3q*V^Y_I!f2 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 diff --git a/bin/Debug/Cat.pdb b/bin/Debug/Cat.pdb deleted file mode 100644 index c534f03d1ad37e0d16c42ec3157d01c490e088ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130560 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 D8F&_d?y zGjwBwf!USw4Vor0P?Yoo+_N+0PMN9a(S7EO;0PlusWOvLhe6a)eFFwuhno_LHxy+s zMqn|c5ozI!m%M>Y1Yp#*t?)!vT2Nt zEHeaz%_ATYRoLbXKxUm1B^y9A&z!J+fS-01I
  • YebVp{mbmd?M;Sf=>#u2F(i8H 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%0Dd!C@sW+e;T^NTdyf!Lm3q*V^Y_I!f2 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 diff --git a/obj/x86/Debug/Cat.pdb b/obj/x86/Debug/Cat.pdb deleted file mode 100644 index c534f03d1ad37e0d16c42ec3157d01c490e088ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130560 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 From 4b674f9b48c9ae799f0e4d202dc371f0a46da72a Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 31 Oct 2014 15:58:45 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cat.Net.sln.DotSettings.user | 2 -- Cat.Net.suo | Bin 49664 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 Cat.Net.sln.DotSettings.user delete mode 100644 Cat.Net.suo diff --git a/Cat.Net.sln.DotSettings.user b/Cat.Net.sln.DotSettings.user deleted file mode 100644 index 8641e71..0000000 --- a/Cat.Net.sln.DotSettings.user +++ /dev/null @@ -1,2 +0,0 @@ - - False \ No newline at end of file diff --git a/Cat.Net.suo b/Cat.Net.suo deleted file mode 100644 index 266729916bd8a871b91322469b0fb82832fa3ee6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49664 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 T{l$2EIAT!v+mUyzR^ Date: Thu, 20 Nov 2014 18:28:52 +0800 Subject: [PATCH 04/21] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=AF=B9IIS6=E7=9A=84?= =?UTF-8?q?=E5=90=91=E4=B8=8B=E5=85=BC=E5=AE=B9=EF=BC=8C=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E5=8A=A8=E6=80=81httpmodule=E6=B3=A8=E5=86=8C=EF=BC=8Ccat?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=B8=BA=E5=85=B3=E9=97=AD=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cat.Net.csproj | 1 + Cat.cs | 382 +++--- Configuration/ClientConfig.cs | 74 +- Configuration/Domain.cs | 66 +- Configuration/Server.cs | 78 +- Message/IEvent.cs | 40 +- Message/IHeartbeat.cs | 52 +- Message/IMessage.cs | 190 +-- Message/IMessageTree.cs | 80 +- Message/ITransaction.cs | 172 +-- Message/Internals/AbstractMessage.cs | 251 ++-- Message/Internals/DefaultEvent.cs | 21 +- Message/Internals/DefaultHeartbeat.cs | 21 +- Message/Internals/DefaultMessageTree.cs | 202 ++-- Message/Internals/DefaultTransaction.cs | 227 ++-- Message/Internals/MessageId.cs | 202 ++-- Message/Internals/NullEvent.cs | 17 +- Message/Internals/NullHeartbeat.cs | 17 +- Message/Internals/NullTransaction.cs | 129 +- Message/Spi/Codec/ChannelBuffer.cs | 282 ++--- Message/Spi/Codec/IMessageCodec.cs | 20 +- Message/Spi/Codec/PlainTextMessageCodec.cs | 1050 ++++++++--------- Message/Spi/IMessageManager.cs | 154 ++- Message/Spi/IMessageProducer.cs | 264 ++--- Message/Spi/IMessageStatistics.cs | 32 +- Message/Spi/IO/IMessageSender.cs | 24 +- Message/Spi/IO/TcpMessageSender.cs | 524 ++++---- .../Spi/Internals/DefaultMessageManager.cs | 685 +++++------ .../Spi/Internals/DefaultMessageProducer.cs | 257 ++-- .../Spi/Internals/DefaultMessageStatistics.cs | 58 +- Message/Spi/Internals/MessageIdFactory.cs | 170 +-- Message/Spi/Internals/StatusUpdateTask.cs | 382 +++--- Util/AppEnv.cs | 17 +- Util/CatHelper.cs | 89 +- Util/CatThreadLocal.cs | 66 +- Util/Logger.cs | 132 +-- Util/MilliSecondTimer.cs | 134 +-- Util/NetworkInterfaceManager.cs | 76 +- Web/CatHttpAsyncHandler.cs | 51 + Web/CatHttpHandler.cs | 19 +- Web/CatHttpModule.cs | 45 +- 41 files changed, 3424 insertions(+), 3329 deletions(-) create mode 100644 Web/CatHttpAsyncHandler.cs diff --git a/Cat.Net.csproj b/Cat.Net.csproj index 96659a5..1157987 100644 --- a/Cat.Net.csproj +++ b/Cat.Net.csproj @@ -92,6 +92,7 @@ + diff --git a/Cat.cs b/Cat.cs index 18619f3..ec50e39 100644 --- a/Cat.cs +++ b/Cat.cs @@ -2,198 +2,204 @@ using Com.Dianping.Cat.Message.Spi; using Com.Dianping.Cat.Message.Spi.Internals; using Com.Dianping.Cat.Util; -using Microsoft.Web.Infrastructure.DynamicModuleHelper; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; - -namespace Com.Dianping.Cat -{ - public class Cat - { - private static readonly Cat Instance = new Cat(); - - private bool _mInitialized; - - private IMessageManager _mManager; - - private IMessageProducer _mProducer; - - private Cat() - { - } - - public static IMessageManager GetManager() - { - return Instance._mManager; - } - - public static IMessageProducer GetProducer() - { - return Instance._mProducer; - } - - public static void Initialize(string configFile) - { - if (Instance._mInitialized) - { - Logger.Warn("Cat can't initialize again with config file(%s), IGNORED!", configFile); - return; - } - - Logger.Info("Initializing Cat .Net Client ..."); - - DefaultMessageManager manager = new DefaultMessageManager(); - ClientConfig clientConfig = LoadClientConfig(configFile); - - manager.InitializeClient(clientConfig); - Instance._mProducer = new DefaultMessageProducer(manager); - Instance._mManager = manager; - Instance._mInitialized = true; - - Logger.Info("Cat .Net Client initialized."); - } - - public static bool IsInitialized() - { - return Instance._mInitialized; +using System.Xml; + +namespace Com.Dianping.Cat +{ + public class Cat + { + private static readonly Cat Instance = new Cat(); + + private bool _mInitialized; + + private IMessageManager _mManager; + + private IMessageProducer _mProducer; + + private Cat() + { + } + + public static IMessageManager GetManager() + { + return Instance._mManager; + } + + public static IMessageProducer GetProducer() + { + return Instance._mProducer; + } + + public static void Initialize() + { + var path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "App_Data\\TCConfig\\CatConfig.xml"); + Initialize(path); + } + + public static void Initialize(string configFile) + { + if (Instance._mInitialized) + { + Logger.Warn("Cat can't initialize again with config file(%s), IGNORED!", configFile); + return; + } + + Logger.Info("Initializing Cat .Net Client ..."); + + DefaultMessageManager manager = new DefaultMessageManager(); + ClientConfig clientConfig = LoadClientConfig(configFile); + + manager.InitializeClient(clientConfig); + Instance._mProducer = new DefaultMessageProducer(manager); + Instance._mManager = manager; + Instance._mInitialized = true; + Logger.Info("Cat .Net Client initialized."); + } + + public static bool IsInitialized() + { + bool isInitialized = Instance._mInitialized; + if (isInitialized && !Instance._mManager.HasContext()) + { + Instance._mManager.Setup(); + } + return isInitialized; + } + + private static ClientConfig LoadClientConfig(string configFile) + { + ClientConfig config = new ClientConfig(); + + if (File.Exists(configFile)) + { + Logger.Info("Use config file({0}).", configFile); + + XmlDocument doc = new XmlDocument(); + + doc.Load(configFile); + + XmlElement root = doc.DocumentElement; + + if (root != null) + { + config.Domain = BuildDomain(root.GetElementsByTagName("domain")); + + IEnumerable servers = BuildServers(root.GetElementsByTagName("servers")); + + //NOTE: 只添加Enabled的 + foreach (Server server in servers.Where(server => server.Enabled)) + { + config.Servers.Add(server); + Logger.Info("CAT server configured: {0}:{1}", server.Ip, server.Port); + } + } + } + else + { + Logger.Warn("Config file({0}) not found.", configFile); + //Logger.Warn("Config file({0}) not found, using localhost:2280 instead.", configFile); + + //config.Domain = BuildDomain(null); + //config.Servers.Add(new Server("localhost", 2280)); + } + + return config; + } + + private static Domain BuildDomain(XmlNodeList nodes) + { + if (nodes == null || nodes.Count == 0) + { + return new Domain(); + } + + XmlElement node = (XmlElement)nodes[0]; + return new Domain + { + Id = GetStringProperty(node, "id", "Unknown"), + //Ip = GetStringProperty(node, "ip", null), + Enabled = GetBooleanProperty(node, "enabled", false) + }; } - public static void RegisterHttpModule() + private static IEnumerable BuildServers(XmlNodeList nodes) { - DynamicModuleUtility.RegisterModule(typeof(Com.Dianping.Cat.Web.CatHttpModule)); - } - - private static ClientConfig LoadClientConfig(string configFile) - { - ClientConfig config = new ClientConfig(); - - if (File.Exists(configFile)) - { - Logger.Info("Use config file({0}).", configFile); - - XmlDocument doc = new XmlDocument(); - - doc.Load(configFile); - - XmlElement root = doc.DocumentElement; - - if (root != null) - { - config.Domain = BuildDomain(root.GetElementsByTagName("domain")); - - IEnumerable servers = BuildServers(root.GetElementsByTagName("servers")); - - //NOTE: 只添加Enabled的 - foreach (Server server in servers.Where(server => server.Enabled)) - { - config.Servers.Add(server); - Logger.Info("CAT server configured: {0}:{1}", server.Ip, server.Port); - } - } - } - else - { - Logger.Warn("Config file({0}) not found, using localhost:2280 instead.", configFile); - - config.Domain = BuildDomain(null); - config.Servers.Add(new Server("localhost", 2280)); - } - - return config; - } - - private static Domain BuildDomain(XmlNodeList nodes) - { - if (nodes == null || nodes.Count == 0) - { - return new Domain(); - } - - XmlElement node = (XmlElement) nodes[0]; - return new Domain - { - Id = GetStringProperty(node, "id", "Unknown"), - //Ip = GetStringProperty(node, "ip", null), - Enabled = GetBooleanProperty(node, "enabled", true) - }; - } - - private static IEnumerable BuildServers(XmlNodeList nodes) - { - List servers = new List(); - - if (nodes != null && nodes.Count > 0) - { - XmlElement first = (XmlElement) nodes[0]; - XmlNodeList serverNodes = first.GetElementsByTagName("server"); - - foreach (XmlNode node in serverNodes) - { - XmlElement serverNode = (XmlElement) node; - string ip = GetStringProperty(serverNode, "ip", "localhost"); - int port = GetIntProperty(serverNode, "port", 2280); - Server server = new Server(ip, port) {Enabled = GetBooleanProperty(serverNode, "enabled", true)}; - - servers.Add(server); - } - } - - if (servers.Count == 0) - { - Logger.Warn("No server configured, use localhost:2280 instead."); - servers.Add(new Server("localhost", 2280)); - } - - return servers; - } - - private static string GetStringProperty(XmlElement element, string name, string defaultValue) - { - if (element != null) - { - string value = element.GetAttribute(name); - - if (value.Length > 0) - { - return value; - } - } - - return defaultValue; - } - - private static bool GetBooleanProperty(XmlElement element, string name, bool defaultValue) - { - if (element != null) - { - string value = element.GetAttribute(name); - - if (value.Length > 0) - { - return "true".Equals(value); - } - } - - return defaultValue; - } - - private static int GetIntProperty(XmlElement element, string name, int defaultValue) - { - if (element != null) - { - string value = element.GetAttribute(name); - - if (value.Length > 0) - { - int tmpRet; - if (int.TryParse(value, out tmpRet)) - return tmpRet; - } - } - - return defaultValue; - } - } + List servers = new List(); + + if (nodes != null && nodes.Count > 0) + { + XmlElement first = (XmlElement)nodes[0]; + XmlNodeList serverNodes = first.GetElementsByTagName("server"); + + foreach (XmlNode node in serverNodes) + { + XmlElement serverNode = (XmlElement)node; + string ip = GetStringProperty(serverNode, "ip", "localhost"); + int port = GetIntProperty(serverNode, "port", 2280); + Server server = new Server(ip, port) { Enabled = GetBooleanProperty(serverNode, "enabled", true) }; + + servers.Add(server); + } + } + + if (servers.Count == 0) + { + Logger.Warn("No server configured, use localhost:2280 instead."); + servers.Add(new Server("localhost", 2280)); + } + + return servers; + } + + private static string GetStringProperty(XmlElement element, string name, string defaultValue) + { + if (element != null) + { + string value = element.GetAttribute(name); + + if (value.Length > 0) + { + return value; + } + } + + return defaultValue; + } + + private static bool GetBooleanProperty(XmlElement element, string name, bool defaultValue) + { + if (element != null) + { + string value = element.GetAttribute(name); + + if (value.Length > 0) + { + return "true".Equals(value); + } + } + + return defaultValue; + } + + private static int GetIntProperty(XmlElement element, string name, int defaultValue) + { + if (element != null) + { + string value = element.GetAttribute(name); + + if (value.Length > 0) + { + int tmpRet; + if (int.TryParse(value, out tmpRet)) + return tmpRet; + } + } + + return defaultValue; + } + + } } \ No newline at end of file diff --git a/Configuration/ClientConfig.cs b/Configuration/ClientConfig.cs index 86e7bf3..4131072 100644 --- a/Configuration/ClientConfig.cs +++ b/Configuration/ClientConfig.cs @@ -1,38 +1,38 @@ -using System.Collections.Generic; - -namespace Com.Dianping.Cat.Configuration -{ - /// - /// Cat客户端配置 - /// - public class ClientConfig - { - private readonly IList _mServers; - private Domain _mDomain; - - public ClientConfig() - { - _mServers = new List(); - } - - /// - /// 是否是开发模式 - /// - public bool DevMode { get; set; } - - public Domain Domain - { - get { return _mDomain ?? (_mDomain = new Domain()); } - - set { _mDomain = value; } - } - - /// - /// Cat日志服务器,可以有多个 - /// - public IList Servers - { - get { return _mServers; } - } - } +using System.Collections.Generic; + +namespace Com.Dianping.Cat.Configuration +{ + /// + /// Cat客户端配置 + /// + public class ClientConfig + { + private readonly IList _mServers; + private Domain _mDomain; + + public ClientConfig() + { + _mServers = new List(); + } + + /// + /// 是否是开发模式 + /// + public bool DevMode { get; set; } + + public Domain Domain + { + get { return _mDomain ?? (_mDomain = new Domain()); } + + set { _mDomain = value; } + } + + /// + /// Cat日志服务器,可以有多个 + /// + public IList Servers + { + get { return _mServers; } + } + } } \ No newline at end of file diff --git a/Configuration/Domain.cs b/Configuration/Domain.cs index 63368ae..8c13f63 100644 --- a/Configuration/Domain.cs +++ b/Configuration/Domain.cs @@ -1,34 +1,34 @@ -namespace Com.Dianping.Cat.Configuration -{ - /// - /// 描述当前系统的情况 - /// - public class Domain - { - private string _id = "Unknown"; - private bool _mEnabled = true; - - /// - /// 当前系统的标识 - /// - public string Id - { - get { return _id; } - set { _id = value; } - } - - ///// - ///// 当前系统的IP - ///// - //public string Ip { get; set; } - - /// - /// Cat日志是否开启,默认开启 - /// - public bool Enabled - { - get { return _mEnabled; } - set { _mEnabled = value; } - } - } +namespace Com.Dianping.Cat.Configuration +{ + /// + /// 描述当前系统的情况 + /// + public class Domain + { + private string _id = "Unknown"; + private bool _mEnabled = false; + + /// + /// 当前系统的标识 + /// + public string Id + { + get { return _id; } + set { _id = value; } + } + + ///// + ///// 当前系统的IP + ///// + //public string Ip { get; set; } + + /// + /// Cat日志是否开启,默认关闭 + /// + public bool Enabled + { + get { return _mEnabled; } + set { _mEnabled = value; } + } + } } \ No newline at end of file diff --git a/Configuration/Server.cs b/Configuration/Server.cs index 5dfebf5..3c01809 100644 --- a/Configuration/Server.cs +++ b/Configuration/Server.cs @@ -1,40 +1,40 @@ -namespace Com.Dianping.Cat.Configuration -{ - /// - /// 描述记录当前系统日志的目标Cat服务器 - /// - public class Server - { - private readonly string _mIp; - - private readonly int _mPort; - - public Server(string ip, int port) - { - _mIp = ip; - _mPort = port; - Enabled = true; - } - - /// - /// Cat服务器IP - /// - public string Ip - { - get { return _mIp; } - } - - /// - /// Cat服务器端口 - /// - public int Port - { - get { return _mPort; } - } - - /// - /// Cat服务器是否有效,默认有效 - /// - public bool Enabled { get; set; } - } +namespace Com.Dianping.Cat.Configuration +{ + /// + /// 描述记录当前系统日志的目标Cat服务器 + /// + public class Server + { + private readonly string _mIp; + + private readonly int _mPort; + + public Server(string ip, int port) + { + _mIp = ip; + _mPort = port; + Enabled = true; + } + + /// + /// Cat服务器IP + /// + public string Ip + { + get { return _mIp; } + } + + /// + /// Cat服务器端口 + /// + public int Port + { + get { return _mPort; } + } + + /// + /// Cat服务器是否有效,默认有效 + /// + public bool Enabled { get; set; } + } } \ No newline at end of file diff --git a/Message/IEvent.cs b/Message/IEvent.cs index 21f006c..e8577fe 100644 --- a/Message/IEvent.cs +++ b/Message/IEvent.cs @@ -1,21 +1,21 @@ -namespace Com.Dianping.Cat.Message -{ - /// - ///

    - /// Event - /// is used to log anything interesting happens at a specific - /// time. Such as an exception thrown, a review added by user, a new user - /// registered, an user logged into the system etc.

    However, if it could be failure, or last for a long time, such as a remote - /// API call, database call or search engine call etc. It should be logged as a - /// Transaction - ///

    All CAT message will be constructed as a message tree and send to back-end - /// for further analysis, and for monitoring. Only - /// Transaction - /// can - /// be a tree node, all other message will be the tree leaf.?The transaction - /// without other messages nested is an atomic transaction.

    - ///
    - public interface IEvent : IMessage - { - } +namespace Com.Dianping.Cat.Message +{ + /// + ///

    + /// Event + /// is used to log anything interesting happens at a specific + /// time. Such as an exception thrown, a review added by user, a new user + /// registered, an user logged into the system etc.

    However, if it could be failure, or last for a long time, such as a remote + /// API call, database call or search engine call etc. It should be logged as a + /// Transaction + ///

    All CAT message will be constructed as a message tree and send to back-end + /// for further analysis, and for monitoring. Only + /// Transaction + /// can + /// be a tree node, all other message will be the tree leaf.?The transaction + /// without other messages nested is an atomic transaction.

    + ///
    + public interface IEvent : IMessage + { + } } \ No newline at end of file diff --git a/Message/IHeartbeat.cs b/Message/IHeartbeat.cs index 290c473..addae00 100644 --- a/Message/IHeartbeat.cs +++ b/Message/IHeartbeat.cs @@ -1,27 +1,27 @@ -namespace Com.Dianping.Cat.Message -{ - /// - ///

    - /// Heartbeat - /// is used to log data that happens in a regular - /// intervals, for example once per second, such as system load, CPU percentage, - /// memory usage, thread pool statistics, cache hit/miss rate, service manifest - /// etc., and even some configuration could be carried by - /// Heartbeat - /// . - /// There could be some good use cases, for example health checker and load - /// balancer, that make good use of it.

    - /// Heartbeat - /// should never be used per request since the request is - /// not regular predictable, instead it could be logged in a daemon background - /// thread, or something like a Timer.

    All CAT message will be constructed as a message tree and send to back-end - /// for further analysis, and for monitoring. Only - /// Transaction - /// can - /// be a tree node, all other message will be the tree leaf.?The transaction - /// without other messages nested is an atomic transaction.

    - ///
    - public interface IHeartbeat : IMessage - { - } +namespace Com.Dianping.Cat.Message +{ + /// + ///

    + /// Heartbeat + /// is used to log data that happens in a regular + /// intervals, for example once per second, such as system load, CPU percentage, + /// memory usage, thread pool statistics, cache hit/miss rate, service manifest + /// etc., and even some configuration could be carried by + /// Heartbeat + /// . + /// There could be some good use cases, for example health checker and load + /// balancer, that make good use of it.

    + /// Heartbeat + /// should never be used per request since the request is + /// not regular predictable, instead it could be logged in a daemon background + /// thread, or something like a Timer.

    All CAT message will be constructed as a message tree and send to back-end + /// for further analysis, and for monitoring. Only + /// Transaction + /// can + /// be a tree node, all other message will be the tree leaf.?The transaction + /// without other messages nested is an atomic transaction.

    + ///
    + public interface IHeartbeat : IMessage + { + } } \ No newline at end of file diff --git a/Message/IMessage.cs b/Message/IMessage.cs index 4deae9f..eec8ff6 100644 --- a/Message/IMessage.cs +++ b/Message/IMessage.cs @@ -1,108 +1,108 @@ -using System; - -namespace Com.Dianping.Cat.Message -{ +using System; + +namespace Com.Dianping.Cat.Message +{ /** - *

    - * Message represents data collected during application runtime. It will be sent - * to back-end system asynchronous for further processing. - *

    - * - *

    - * Super interface of Event, Heartbeat and - * Transaction. - *

    - * - * @see Event, Heartbeat, Transaction - * @author Frankie Wu - */ - - public interface IMessage - { + *

    + * Message represents data collected during application runtime. It will be sent + * to back-end system asynchronous for further processing. + *

    + * + *

    + * Super interface of Event, Heartbeat and + * Transaction. + *

    + * + * @see Event, Heartbeat, Transaction + * @author Frankie Wu + */ + + public interface IMessage + { /** - * @return key value pairs data - */ - string Data { get; } - + * @return key value pairs data + */ + string Data { get; } + /** - * Message name. - * - * @return message name - */ - string Name { get; } - + * Message name. + * + * @return message name + */ + string Name { get; } + /** - * Get the message status. - * - * @return message status. "0" means success, otherwise error code. - */ - string Status { get; set; } - + * Get the message status. + * + * @return message status. "0" means success, otherwise error code. + */ + string Status { get; set; } + /** - * The time stamp the message was created. - * - * @return message creation time stamp in milliseconds - */ - long Timestamp { get; set; } - + * The time stamp the message was created. + * + * @return message creation time stamp in milliseconds + */ + long Timestamp { get; set; } + /** - * Message type. - * - *

    - * Typical message types are: - *

      - *
    • URL: maps to one method of an action
    • - *
    • Service: maps to one method of service call
    • - *
    • Search: maps to one method of search call
    • - *
    • SQL: maps to one SQL statement
    • - *
    • Cache: maps to one cache access
    • - *
    • Error: maps to java.lang.Throwable (java.lang.Exception and java.lang.Error)
    • - *
    - *

    - * - * @return message type - */ - string Type { get; } - + * Message type. + * + *

    + * Typical message types are: + *

      + *
    • URL: maps to one method of an action
    • + *
    • Service: maps to one method of service call
    • + *
    • Search: maps to one method of search call
    • + *
    • SQL: maps to one SQL statement
    • + *
    • Cache: maps to one cache access
    • + *
    • Error: maps to java.lang.Throwable (java.lang.Exception and java.lang.Error)
    • + *
    + *

    + * + * @return message type + */ + string Type { get; } + /** - * add one or multiple key-value pairs to the message. - * - * @param keyValuePairs - * key-value pairs like 'a=1&b=2&...' - */ - void AddData(String keyValuePairs); - + * add one or multiple key-value pairs to the message. + * + * @param keyValuePairs + * key-value pairs like 'a=1&b=2&...' + */ + void AddData(String keyValuePairs); + /** - * add one key-value pair to the message. - * - * @param key - * @param value - */ - void AddData(String key, Object value); - + * add one key-value pair to the message. + * + * @param key + * @param value + */ + void AddData(String key, Object value); + /** - * Complete the message construction. - */ - void Complete(); - + * Complete the message construction. + */ + void Complete(); + /** - * If the complete() method was called or not. - * - * @return true means the complete() method was called, false otherwise. - */ - bool IsCompleted(); - + * If the complete() method was called or not. + * + * @return true means the complete() method was called, false otherwise. + */ + bool IsCompleted(); + /** - * @return - */ - bool IsSuccess(); - + * @return + */ + bool IsSuccess(); + /** - * Set the message status with exception class name. - * - * @param e - * exception. - */ - void SetStatus(Exception e); - } + * Set the message status with exception class name. + * + * @param e + * exception. + */ + void SetStatus(Exception e); + } } \ No newline at end of file diff --git a/Message/IMessageTree.cs b/Message/IMessageTree.cs index 78f8f2f..b9a75e7 100644 --- a/Message/IMessageTree.cs +++ b/Message/IMessageTree.cs @@ -1,41 +1,41 @@ -using System; - -namespace Com.Dianping.Cat.Message -{ - public interface IMessageTree - { - String Domain { get; set; } - - - String HostName { get; set; } - - - String IpAddress { get; set; } - - - IMessage Message { get; set; } - - - String MessageId { get; set; } - - - String ParentMessageId { get; set; } - - - String RootMessageId { get; set; } - - - String SessionToken { get; set; } - - - String ThreadGroupName { get; set; } - - - String ThreadId { get; set; } - - - String ThreadName { get; set; } - - IMessageTree Copy(); - } +using System; + +namespace Com.Dianping.Cat.Message +{ + public interface IMessageTree + { + String Domain { get; set; } + + + String HostName { get; set; } + + + String IpAddress { get; set; } + + + IMessage Message { get; set; } + + + String MessageId { get; set; } + + + String ParentMessageId { get; set; } + + + String RootMessageId { get; set; } + + + String SessionToken { get; set; } + + + String ThreadGroupName { get; set; } + + + String ThreadId { get; set; } + + + String ThreadName { get; set; } + + IMessageTree Copy(); + } } \ No newline at end of file diff --git a/Message/ITransaction.cs b/Message/ITransaction.cs index 21973e4..7f7b212 100644 --- a/Message/ITransaction.cs +++ b/Message/ITransaction.cs @@ -1,94 +1,94 @@ -using System.Collections.Generic; - -namespace Com.Dianping.Cat.Message -{ +using System.Collections.Generic; + +namespace Com.Dianping.Cat.Message +{ /** - *

    - * Transaction is any interesting unit of work that takes time to - * complete and may fail. - *

    - * - *

    - * Basically, all data access across the boundary needs to be logged as a - * Transaction since it may fail and time consuming. For example, - * URL request, disk IO, JDBC query, search query, HTTP request, 3rd party API - * call etc. - *

    - * - *

    - * Sometime if A needs call B which is owned by another team, although A and B - * are deployed together without any physical boundary. To make the ownership - * clear, there could be some Transaction logged when A calls B. - *

    - * - *

    - * Most of Transaction should be logged in the infrastructure level - * or framework level, which is transparent to the application. - *

    - * - *

    - * All CAT message will be constructed as a message tree and send to back-end - * for further analysis, and for monitoring. Only Transaction can - * be a tree node, all other message will be the tree leaf. The transaction - * without other messages nested is an atomic transaction. - *

    - * - * @author Frankie Wu - */ - - public interface ITransaction : IMessage - { + *

    + * Transaction is any interesting unit of work that takes time to + * complete and may fail. + *

    + * + *

    + * Basically, all data access across the boundary needs to be logged as a + * Transaction since it may fail and time consuming. For example, + * URL request, disk IO, JDBC query, search query, HTTP request, 3rd party API + * call etc. + *

    + * + *

    + * Sometime if A needs call B which is owned by another team, although A and B + * are deployed together without any physical boundary. To make the ownership + * clear, there could be some Transaction logged when A calls B. + *

    + * + *

    + * Most of Transaction should be logged in the infrastructure level + * or framework level, which is transparent to the application. + *

    + * + *

    + * All CAT message will be constructed as a message tree and send to back-end + * for further analysis, and for monitoring. Only Transaction can + * be a tree node, all other message will be the tree leaf. The transaction + * without other messages nested is an atomic transaction. + *

    + * + * @author Frankie Wu + */ + + public interface ITransaction : IMessage + { /** - * Get all children message within current transaction. - * - *

    - * Typically, a Transaction can nest other - * Transactions, Events and Heartbeat - * s, while an Event or Heartbeat can't nest other - * messages. - *

    - * - * @return all children messages, empty if there is no nested children. - */ - IList Children { get; } - + * Get all children message within current transaction. + * + *

    + * Typically, a Transaction can nest other + * Transactions, Events and Heartbeat + * s, while an Event or Heartbeat can't nest other + * messages. + *

    + * + * @return all children messages, empty if there is no nested children. + */ + IList Children { get; } + /** - * How long the transaction took from construction to complete. Time unit is - * microsecond. - * - * @return duration time in microsecond - */ - long DurationInMicros { get; set; } - + * How long the transaction took from construction to complete. Time unit is + * microsecond. + * + * @return duration time in microsecond + */ + long DurationInMicros { get; set; } + /** - * How long the transaction took from construction to complete. Time unit is - * millisecond. - * - * @return duration time in millisecond - */ - long DurationInMillis { get; set; } - + * How long the transaction took from construction to complete. Time unit is + * millisecond. + * + * @return duration time in millisecond + */ + long DurationInMillis { get; set; } + /** - * Check if the transaction is stand-alone or belongs to another one. - * - * @return true if it's an root transaction. - */ - bool Standalone { get; set; } - + * Check if the transaction is stand-alone or belongs to another one. + * + * @return true if it's an root transaction. + */ + bool Standalone { get; set; } + /** - * Add one nested child message to current transaction. - * - * @param message - * to be added - */ - ITransaction AddChild(IMessage message); - + * Add one nested child message to current transaction. + * + * @param message + * to be added + */ + ITransaction AddChild(IMessage message); + /** - * Has children or not. An atomic transaction does not have any children - * message. - * - * @return true if child exists, else false. - */ - bool HasChildren(); - } + * Has children or not. An atomic transaction does not have any children + * message. + * + * @return true if child exists, else false. + */ + bool HasChildren(); + } } \ No newline at end of file diff --git a/Message/Internals/AbstractMessage.cs b/Message/Internals/AbstractMessage.cs index 5d5345a..1096f7a 100644 --- a/Message/Internals/AbstractMessage.cs +++ b/Message/Internals/AbstractMessage.cs @@ -1,126 +1,127 @@ -using System; -using System.Text; -using Com.Dianping.Cat.Message.Spi.Codec; -using Com.Dianping.Cat.Util; - -namespace Com.Dianping.Cat.Message.Internals -{ - public abstract class AbstractMessage : IMessage - { - private readonly String _mName; - private readonly String _mType; - private bool _mCompleted; - private StringBuilder _mData; - - private String _mStatus = "unset"; - - protected AbstractMessage(String type, String name) - { - _mType = type; - _mName = name; - TimestampInMicros = MilliSecondTimer.CurrentTimeMicros(); - } - - /// - /// 其实是Ticks除以10 - /// - protected long TimestampInMicros { get; private set; } - - #region IMessage Members - - public String Data - { - get { return _mData == null ? "" : _mData.ToString(); } - } - - public String Name - { - get { return _mName; } - } - - public String Status - { - get { return _mStatus; } - - set { _mStatus = value; } - } - - /// - /// 其实是Ticks除以10000 - /// - public long Timestamp - { - get { return TimestampInMicros/1000L; } - set { TimestampInMicros = value*1000L; } - } - - public String Type - { - get { return _mType; } - } - - public void AddData(String keyValuePairs) - { - if (_mData == null) - { - _mData = new StringBuilder(keyValuePairs); - } - else - { - _mData.Append(keyValuePairs); - } - } - - public void AddData(String key, Object value) - { - if (_mData == null) - { - _mData = new StringBuilder(); - } - else if (_mData.Length > 0) - { - _mData.Append('&'); - } - - _mData.Append(key).Append('=').Append(value); - } - - public virtual void Complete() - { - SetCompleted(true); - } - - public bool IsCompleted() - { - return _mCompleted; - } - - public bool IsSuccess() - { - return "0".Equals(_mStatus); - } - - public void SetStatus(Exception e) - { - _mStatus = e.GetType().FullName; - } - - #endregion - - protected void SetCompleted(bool completed) - { - _mCompleted = completed; - } - - public override String ToString() - { - PlainTextMessageCodec codec = new PlainTextMessageCodec(); - ChannelBuffer buf = new ChannelBuffer(8192); - - codec.EncodeMessage(this, buf); - buf.Reset(); - - return buf.ToString(); - } - } +using System; +using System.Text; +using Com.Dianping.Cat.Message.Spi.Codec; +using Com.Dianping.Cat.Util; + +namespace Com.Dianping.Cat.Message.Internals +{ + [Serializable] + public abstract class AbstractMessage : IMessage + { + private readonly String _mName; + private readonly String _mType; + private bool _mCompleted; + private StringBuilder _mData; + + private String _mStatus = "unset"; + + protected AbstractMessage(String type, String name) + { + _mType = type; + _mName = name; + TimestampInMicros = MilliSecondTimer.CurrentTimeMicros(); + } + + /// + /// 其实是Ticks除以10 + /// + protected long TimestampInMicros { get; private set; } + + #region IMessage Members + + public String Data + { + get { return _mData == null ? "" : _mData.ToString(); } + } + + public String Name + { + get { return _mName; } + } + + public String Status + { + get { return _mStatus; } + + set { _mStatus = value; } + } + + /// + /// 其实是Ticks除以10000 + /// + public long Timestamp + { + get { return TimestampInMicros / 1000L; } + set { TimestampInMicros = value * 1000L; } + } + + public String Type + { + get { return _mType; } + } + + public void AddData(String keyValuePairs) + { + if (_mData == null) + { + _mData = new StringBuilder(keyValuePairs); + } + else + { + _mData.Append(keyValuePairs); + } + } + + public void AddData(String key, Object value) + { + if (_mData == null) + { + _mData = new StringBuilder(); + } + else if (_mData.Length > 0) + { + _mData.Append('&'); + } + + _mData.Append(key).Append('=').Append(value); + } + + public virtual void Complete() + { + SetCompleted(true); + } + + public bool IsCompleted() + { + return _mCompleted; + } + + public bool IsSuccess() + { + return "0".Equals(_mStatus); + } + + public void SetStatus(Exception e) + { + _mStatus = e.GetType().FullName; + } + + #endregion + + protected void SetCompleted(bool completed) + { + _mCompleted = completed; + } + + public override String ToString() + { + PlainTextMessageCodec codec = new PlainTextMessageCodec(); + ChannelBuffer buf = new ChannelBuffer(8192); + + codec.EncodeMessage(this, buf); + buf.Reset(); + + return buf.ToString(); + } + } } \ No newline at end of file diff --git a/Message/Internals/DefaultEvent.cs b/Message/Internals/DefaultEvent.cs index 450cf8d..3336aae 100644 --- a/Message/Internals/DefaultEvent.cs +++ b/Message/Internals/DefaultEvent.cs @@ -1,11 +1,12 @@ -using System; - -namespace Com.Dianping.Cat.Message.Internals -{ - public class DefaultEvent : AbstractMessage, IEvent - { - public DefaultEvent(String type, String name) : base(type, name) - { - } - } +using System; + +namespace Com.Dianping.Cat.Message.Internals +{ + public class DefaultEvent : AbstractMessage, IEvent + { + public DefaultEvent(String type, String name) + : base(type, name) + { + } + } } \ No newline at end of file diff --git a/Message/Internals/DefaultHeartbeat.cs b/Message/Internals/DefaultHeartbeat.cs index 9725603..cffe0fe 100644 --- a/Message/Internals/DefaultHeartbeat.cs +++ b/Message/Internals/DefaultHeartbeat.cs @@ -1,11 +1,12 @@ -using System; - -namespace Com.Dianping.Cat.Message.Internals -{ - public class DefaultHeartbeat : AbstractMessage, IHeartbeat - { - public DefaultHeartbeat(String type, String name) : base(type, name) - { - } - } +using System; + +namespace Com.Dianping.Cat.Message.Internals +{ + public class DefaultHeartbeat : AbstractMessage, IHeartbeat + { + public DefaultHeartbeat(String type, String name) + : base(type, name) + { + } + } } \ No newline at end of file diff --git a/Message/Internals/DefaultMessageTree.cs b/Message/Internals/DefaultMessageTree.cs index 047ea7c..b75a433 100644 --- a/Message/Internals/DefaultMessageTree.cs +++ b/Message/Internals/DefaultMessageTree.cs @@ -1,102 +1,102 @@ -using Com.Dianping.Cat.Message.Spi.Codec; -using System; - -namespace Com.Dianping.Cat.Message.Internals -{ - public class DefaultMessageTree : IMessageTree - { - private String _mMessageId; - - private String _mParentMessageId; - - private String _mRootMessageId; - - #region IMessageTree Members - - public IMessageTree Copy() - { - DefaultMessageTree tree = new DefaultMessageTree(); - - tree.Domain = Domain; - tree.HostName = HostName; - tree.IpAddress = IpAddress; - tree.MessageId = _mMessageId; - tree.ParentMessageId = _mParentMessageId; - tree.RootMessageId = _mRootMessageId; - tree.SessionToken = SessionToken; - tree.ThreadGroupName = ThreadGroupName; - tree.ThreadId = ThreadId; - tree.ThreadName = ThreadName; - tree.Message = Message; - - return tree; - } - - public string Domain { get; set; } - - public string HostName { get; set; } - - public string IpAddress { get; set; } - - public IMessage Message { get; set; } - - public String MessageId - { - get { return _mMessageId; } - set - { - if (!string.IsNullOrEmpty(value)) - { - _mMessageId = value; - } - } - } - - public String ParentMessageId - { - get { return _mParentMessageId; } - set - { - if (!string.IsNullOrEmpty(value)) - { - _mParentMessageId = value; - } - } - } - - public String RootMessageId - { - get { return _mRootMessageId; } - set - { - if (!string.IsNullOrEmpty(value)) - { - _mRootMessageId = value; - } - } - } - - public string SessionToken { get; set; } - - public string ThreadGroupName { get; set; } - - public string ThreadId { get; set; } - - public string ThreadName { get; set; } - - #endregion - - public override String ToString() - { - PlainTextMessageCodec codec = new PlainTextMessageCodec(); - ChannelBuffer buf = new ChannelBuffer(8192); - - codec.Encode(this, buf); - - buf.Reset(); - buf.Skip(4); // get rid of length - - return buf.ToString(); - } - } +using Com.Dianping.Cat.Message.Spi.Codec; +using System; + +namespace Com.Dianping.Cat.Message.Internals +{ + public class DefaultMessageTree : IMessageTree + { + private String _mMessageId; + + private String _mParentMessageId; + + private String _mRootMessageId; + + #region IMessageTree Members + + public IMessageTree Copy() + { + DefaultMessageTree tree = new DefaultMessageTree(); + + tree.Domain = Domain; + tree.HostName = HostName; + tree.IpAddress = IpAddress; + tree.MessageId = _mMessageId; + tree.ParentMessageId = _mParentMessageId; + tree.RootMessageId = _mRootMessageId; + tree.SessionToken = SessionToken; + tree.ThreadGroupName = ThreadGroupName; + tree.ThreadId = ThreadId; + tree.ThreadName = ThreadName; + tree.Message = Message; + + return tree; + } + + public string Domain { get; set; } + + public string HostName { get; set; } + + public string IpAddress { get; set; } + + public IMessage Message { get; set; } + + public String MessageId + { + get { return _mMessageId; } + set + { + if (!string.IsNullOrEmpty(value)) + { + _mMessageId = value; + } + } + } + + public String ParentMessageId + { + get { return _mParentMessageId; } + set + { + if (!string.IsNullOrEmpty(value)) + { + _mParentMessageId = value; + } + } + } + + public String RootMessageId + { + get { return _mRootMessageId; } + set + { + if (!string.IsNullOrEmpty(value)) + { + _mRootMessageId = value; + } + } + } + + public string SessionToken { get; set; } + + public string ThreadGroupName { get; set; } + + public string ThreadId { get; set; } + + public string ThreadName { get; set; } + + #endregion + + public override String ToString() + { + PlainTextMessageCodec codec = new PlainTextMessageCodec(); + ChannelBuffer buf = new ChannelBuffer(8192); + + codec.Encode(this, buf); + + buf.Reset(); + buf.Skip(4); // get rid of length + + return buf.ToString(); + } + } } \ No newline at end of file diff --git a/Message/Internals/DefaultTransaction.cs b/Message/Internals/DefaultTransaction.cs index b71f4ed..d08d0c3 100644 --- a/Message/Internals/DefaultTransaction.cs +++ b/Message/Internals/DefaultTransaction.cs @@ -1,113 +1,116 @@ -using Com.Dianping.Cat.Util; -using System; -using System.Collections.Generic; - -namespace Com.Dianping.Cat.Message.Internals -{ - public class DefaultTransaction : AbstractMessage, ITransaction - { - private readonly Action _endCallBack; - private IList _mChildren; - private long _mDurationInMicro; // must be less than 0 - - public DefaultTransaction(String type, String name, Action endCallBack) - : base(type, name) - { - _mDurationInMicro = -1; - _endCallBack = endCallBack; - Standalone = true; - } - - #region ITransaction Members - - public IList Children - { - get { return _mChildren ?? (_mChildren = new List()); } - } - - public long DurationInMicros - { - get - { - if (_mDurationInMicro >= 0) - { - return _mDurationInMicro; - } - // if it's not completed explicitly - long duration = 0; - int len = (_mChildren == null) ? 0 : _mChildren.Count; - - if (len > 0) - { - if (_mChildren != null) - { - IMessage lastChild = _mChildren[len - 1]; - - if (lastChild is ITransaction) - { - ITransaction trx = lastChild as ITransaction; - - duration = trx.Timestamp*1000L + trx.DurationInMicros - TimestampInMicros; - } - else - { - duration = lastChild.Timestamp*1000L - TimestampInMicros; - } - } - } - - return duration; - } - set { _mDurationInMicro = value; } - } - - public long DurationInMillis - { - get { return DurationInMicros/1000L; } - set { _mDurationInMicro = value*1000L; } - } - - public bool Standalone { get; set; } - - public ITransaction AddChild(IMessage message) - { - if (_mChildren == null) - { - _mChildren = new List(); - } - - _mChildren.Add(message); - return this; - } - - public override void Complete() - { - if (IsCompleted()) - { - // complete() was called more than once - IMessage evt0 = new DefaultEvent("CAT", "BadInstrument") {Status = "TransactionAlreadyCompleted"}; - - evt0.Complete(); - AddChild(evt0); - } - else - { - _mDurationInMicro = MilliSecondTimer.CurrentTimeMicros() - TimestampInMicros; - - SetCompleted(true); - - if (_endCallBack != null) - { - _endCallBack(this); - } - } - } - - public bool HasChildren() - { - return _mChildren != null && _mChildren.Count > 0; - } - - #endregion - } +using Com.Dianping.Cat.Util; +//using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace Com.Dianping.Cat.Message.Internals +{ + [Serializable] + public class DefaultTransaction : AbstractMessage, ITransaction + { + private readonly Action _endCallBack; + private IList _mChildren; + private long _mDurationInMicro; // must be less than 0 + + public DefaultTransaction(String type, String name, Action endCallBack) + : base(type, name) + { + _mDurationInMicro = -1; + _endCallBack = endCallBack; + Standalone = true; + } + + #region ITransaction Members + + //[JsonConverter(typeof(List))] + public IList Children + { + get { return _mChildren ?? (_mChildren = new List()); } + } + + public long DurationInMicros + { + get + { + if (_mDurationInMicro >= 0) + { + return _mDurationInMicro; + } + // if it's not completed explicitly + long duration = 0; + int len = (_mChildren == null) ? 0 : _mChildren.Count; + + if (len > 0) + { + if (_mChildren != null) + { + IMessage lastChild = _mChildren[len - 1]; + + if (lastChild is ITransaction) + { + ITransaction trx = lastChild as ITransaction; + + duration = trx.Timestamp * 1000L + trx.DurationInMicros - TimestampInMicros; + } + else + { + duration = lastChild.Timestamp * 1000L - TimestampInMicros; + } + } + } + + return duration; + } + set { _mDurationInMicro = value; } + } + + public long DurationInMillis + { + get { return DurationInMicros / 1000L; } + set { _mDurationInMicro = value * 1000L; } + } + + public bool Standalone { get; set; } + + public ITransaction AddChild(IMessage message) + { + if (_mChildren == null) + { + _mChildren = new List(); + } + + _mChildren.Add(message); + return this; + } + + public override void Complete() + { + if (IsCompleted()) + { + // complete() was called more than once + IMessage evt0 = new DefaultEvent("CAT", "BadInstrument") { Status = "TransactionAlreadyCompleted" }; + + evt0.Complete(); + AddChild(evt0); + } + else + { + _mDurationInMicro = MilliSecondTimer.CurrentTimeMicros() - TimestampInMicros; + + SetCompleted(true); + + if (_endCallBack != null) + { + _endCallBack(this); + } + } + } + + public bool HasChildren() + { + return _mChildren != null && _mChildren.Count > 0; + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Internals/MessageId.cs b/Message/Internals/MessageId.cs index e1a501b..9f263fe 100644 --- a/Message/Internals/MessageId.cs +++ b/Message/Internals/MessageId.cs @@ -1,102 +1,102 @@ -using System.Globalization; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Com.Dianping.Cat.Message.Internals -{ - [Obsolete] - public class MessageId - { - private readonly String _mDomain; - - private readonly int _mIndex; - - private readonly String _mIpAddressInHex; - - private readonly long _mTimestamp; - - internal MessageId(String domain, String ipAddressInHex, long timestamp, int index) - { - _mDomain = domain; - _mIpAddressInHex = ipAddressInHex; - _mTimestamp = timestamp; - _mIndex = index; - } - - public String Domain - { - get { return _mDomain; } - } - - public int Index - { - get { return _mIndex; } - } - - public String IpAddressInHex - { - get { return _mIpAddressInHex; } - } - - public long Timestamp - { - get { return _mTimestamp; } - } - - public static MessageId Parse(String messageId) - { - IList list = messageId.Split('-'); - int len = list.Count; - - if (len >= 4) - { - String ipAddressInHex = list[len - 3]; - long timestamp = (Int64.Parse(list[len - 2], NumberStyles.Integer)); - int index = Int32.Parse(list[len - 1]); - String domain; - - if (len > 4) - { - // allow domain contains '-' - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < len - 3; i++) - { - if (i > 0) - { - sb.Append('-'); - } - - sb.Append(list[i]); - } - - domain = sb.ToString(); - } - else - { - domain = list[0]; - } - - return new MessageId(domain, ipAddressInHex, timestamp, index); - } - - throw new Exception("Invalid message id format: " + messageId); - } - - public override String ToString() - { - StringBuilder sb = new StringBuilder(_mDomain.Length + 32); - - sb.Append(_mDomain); - sb.Append('-'); - sb.Append(_mIpAddressInHex); - sb.Append('-'); - sb.Append(_mTimestamp); - sb.Append('-'); - sb.Append(_mIndex); - - return sb.ToString(); - } - } +using System.Globalization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Com.Dianping.Cat.Message.Internals +{ + [Obsolete] + public class MessageId + { + private readonly String _mDomain; + + private readonly int _mIndex; + + private readonly String _mIpAddressInHex; + + private readonly long _mTimestamp; + + internal MessageId(String domain, String ipAddressInHex, long timestamp, int index) + { + _mDomain = domain; + _mIpAddressInHex = ipAddressInHex; + _mTimestamp = timestamp; + _mIndex = index; + } + + public String Domain + { + get { return _mDomain; } + } + + public int Index + { + get { return _mIndex; } + } + + public String IpAddressInHex + { + get { return _mIpAddressInHex; } + } + + public long Timestamp + { + get { return _mTimestamp; } + } + + public static MessageId Parse(String messageId) + { + IList list = messageId.Split('-'); + int len = list.Count; + + if (len >= 4) + { + String ipAddressInHex = list[len - 3]; + long timestamp = (Int64.Parse(list[len - 2], NumberStyles.Integer)); + int index = Int32.Parse(list[len - 1]); + String domain; + + if (len > 4) + { + // allow domain contains '-' + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < len - 3; i++) + { + if (i > 0) + { + sb.Append('-'); + } + + sb.Append(list[i]); + } + + domain = sb.ToString(); + } + else + { + domain = list[0]; + } + + return new MessageId(domain, ipAddressInHex, timestamp, index); + } + + throw new Exception("Invalid message id format: " + messageId); + } + + public override String ToString() + { + StringBuilder sb = new StringBuilder(_mDomain.Length + 32); + + sb.Append(_mDomain); + sb.Append('-'); + sb.Append(_mIpAddressInHex); + sb.Append('-'); + sb.Append(_mTimestamp); + sb.Append('-'); + sb.Append(_mIndex); + + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/Message/Internals/NullEvent.cs b/Message/Internals/NullEvent.cs index a37d544..a631d46 100644 --- a/Message/Internals/NullEvent.cs +++ b/Message/Internals/NullEvent.cs @@ -1,9 +1,10 @@ -namespace Com.Dianping.Cat.Message.Internals -{ - public class NullEvent : AbstractMessage, IEvent - { - public NullEvent() : base(null, null) - { - } - } +namespace Com.Dianping.Cat.Message.Internals +{ + public class NullEvent : AbstractMessage, IEvent + { + public NullEvent() + : base(null, null) + { + } + } } \ No newline at end of file diff --git a/Message/Internals/NullHeartbeat.cs b/Message/Internals/NullHeartbeat.cs index ea39e40..f273d14 100644 --- a/Message/Internals/NullHeartbeat.cs +++ b/Message/Internals/NullHeartbeat.cs @@ -1,9 +1,10 @@ -namespace Com.Dianping.Cat.Message.Internals -{ - public class NullHeartbeat : AbstractMessage, IHeartbeat - { - public NullHeartbeat() : base(null, null) - { - } - } +namespace Com.Dianping.Cat.Message.Internals +{ + public class NullHeartbeat : AbstractMessage, IHeartbeat + { + public NullHeartbeat() + : base(null, null) + { + } + } } \ No newline at end of file diff --git a/Message/Internals/NullTransaction.cs b/Message/Internals/NullTransaction.cs index 920ce9e..aa73f89 100644 --- a/Message/Internals/NullTransaction.cs +++ b/Message/Internals/NullTransaction.cs @@ -1,65 +1,66 @@ -using System.Collections.Generic; - -namespace Com.Dianping.Cat.Message.Internals -{ - public class NullTransaction : AbstractMessage, ITransaction - { - private IList _mChildren; - - public NullTransaction() : base(null, null) - { - } - - #region ITransaction Members - - public IList Children - { - get { return _mChildren ?? (_mChildren = new List()); } - } - - public long DurationInMicros - { - get { return 0; } - set - { - //do nothing here - } - } - - public long DurationInMillis - { - get { return 0; } - set - { - //do nothing here - } - } - - public bool Standalone - { - get { return true; } - set - { - //do nothing here - } - } - - public ITransaction AddChild(IMessage message) - { - // do nothing here - return this; - } - - public override void Complete() - { - // do nothing here - } - - public bool HasChildren() - { - return false; - } - - #endregion - } +using System.Collections.Generic; + +namespace Com.Dianping.Cat.Message.Internals +{ + public class NullTransaction : AbstractMessage, ITransaction + { + private IList _mChildren; + + public NullTransaction() + : base(null, null) + { + } + + #region ITransaction Members + + public IList Children + { + get { return _mChildren ?? (_mChildren = new List()); } + } + + public long DurationInMicros + { + get { return 0; } + set + { + //do nothing here + } + } + + public long DurationInMillis + { + get { return 0; } + set + { + //do nothing here + } + } + + public bool Standalone + { + get { return true; } + set + { + //do nothing here + } + } + + public ITransaction AddChild(IMessage message) + { + // do nothing here + return this; + } + + public override void Complete() + { + // do nothing here + } + + public bool HasChildren() + { + return false; + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Spi/Codec/ChannelBuffer.cs b/Message/Spi/Codec/ChannelBuffer.cs index 6e99b4d..408fcc3 100644 --- a/Message/Spi/Codec/ChannelBuffer.cs +++ b/Message/Spi/Codec/ChannelBuffer.cs @@ -1,142 +1,142 @@ -using System.Text; -using System.IO; - -namespace Com.Dianping.Cat.Message.Spi.Codec -{ - public class ChannelBuffer - { - private readonly MemoryStream _mBuf; - - private readonly BinaryWriter _mWriter; - - public ChannelBuffer(int capacity) - { - _mBuf = new MemoryStream(capacity); - _mWriter = new BinaryWriter(_mBuf, Encoding.UTF8); - } - - /// - /// 从当前位置到目标字符第一次出现的位置有多少字节? - /// - /// - /// - public int BytesBefore(byte separator) - { - int count = 0; - long oldPosition = _mBuf.Position; - - while (_mBuf.Position < _mBuf.Length) - { - int b = _mBuf.ReadByte(); - - if (b == -1) - { - return -1; - } - if ((byte) b == separator) - { - _mBuf.Position = oldPosition; - return count; - } - - count++; - } - - _mBuf.Position = oldPosition; - return 0; - } - - public void Skip(int bytes) - { - _mBuf.Position += bytes; - } - - public int ReadableBytes() - { - return (int) (_mBuf.Length - _mBuf.Position); - } - - public int ReadBytes(byte[] data) - { - return _mBuf.Read(data, 0, data.Length); - } - - public byte ReadByte() - { - return (byte) (_mBuf.ReadByte() & 0xFF); - } - - public void WriteByte(byte b) - { - _mWriter.Write(b); - } - - public void WriteByte(char c) - { - _mWriter.Write((byte) (c & 0xFF)); - } - - public void WriteInt(int i) - { - _mWriter.Write(ToBytes(i)); - } - - public void WriteBytes(byte[] data) - { - _mWriter.Write(data); - } - - public void WriteBytes(byte[] data, int offset, int len) - { - _mWriter.Write(data, offset, len); - } - - // for test purpose - public void Reset() - { - _mBuf.Seek(0, SeekOrigin.Begin); - } - - /// - /// 在流的相应位置插入一个整数的字节(覆盖?) - /// - /// - /// - public void SetInt(int index, int i) - { - _mWriter.Seek(index, SeekOrigin.Begin); - _mWriter.Write(ToBytes(i)); - } - - private static byte[] ToBytes(int value) - { - byte[] bytes = new byte[4]; - - bytes[3] = (byte) value; - bytes[2] = (byte) (value >> 8); - bytes[1] = (byte) (value >> 16); - bytes[0] = (byte) (value >> 24); - return bytes; - } - - public byte[] ToArray() - { - return _mBuf.ToArray(); - } - - /// - /// 从当前位置到结尾的字节数组的字符串表示 - /// - /// - public override string ToString() - { - byte[] data = _mBuf.ToArray(); - int offset = (int) _mBuf.Position; - string str = Encoding.UTF8.GetString(data, offset, data.Length - offset); - - //ToArray本身就不为该Position,所以下一行代码多余 - //_mBuf.Seek(offset, SeekOrigin.Begin); - return str; - } - } +using System.Text; +using System.IO; + +namespace Com.Dianping.Cat.Message.Spi.Codec +{ + public class ChannelBuffer + { + private readonly MemoryStream _mBuf; + + private readonly BinaryWriter _mWriter; + + public ChannelBuffer(int capacity) + { + _mBuf = new MemoryStream(capacity); + _mWriter = new BinaryWriter(_mBuf, Encoding.UTF8); + } + + /// + /// 从当前位置到目标字符第一次出现的位置有多少字节? + /// + /// + /// + public int BytesBefore(byte separator) + { + int count = 0; + long oldPosition = _mBuf.Position; + + while (_mBuf.Position < _mBuf.Length) + { + int b = _mBuf.ReadByte(); + + if (b == -1) + { + return -1; + } + if ((byte)b == separator) + { + _mBuf.Position = oldPosition; + return count; + } + + count++; + } + + _mBuf.Position = oldPosition; + return 0; + } + + public void Skip(int bytes) + { + _mBuf.Position += bytes; + } + + public int ReadableBytes() + { + return (int)(_mBuf.Length - _mBuf.Position); + } + + public int ReadBytes(byte[] data) + { + return _mBuf.Read(data, 0, data.Length); + } + + public byte ReadByte() + { + return (byte)(_mBuf.ReadByte() & 0xFF); + } + + public void WriteByte(byte b) + { + _mWriter.Write(b); + } + + public void WriteByte(char c) + { + _mWriter.Write((byte)(c & 0xFF)); + } + + public void WriteInt(int i) + { + _mWriter.Write(ToBytes(i)); + } + + public void WriteBytes(byte[] data) + { + _mWriter.Write(data); + } + + public void WriteBytes(byte[] data, int offset, int len) + { + _mWriter.Write(data, offset, len); + } + + // for test purpose + public void Reset() + { + _mBuf.Seek(0, SeekOrigin.Begin); + } + + /// + /// 在流的相应位置插入一个整数的字节(覆盖?) + /// + /// + /// + public void SetInt(int index, int i) + { + _mWriter.Seek(index, SeekOrigin.Begin); + _mWriter.Write(ToBytes(i)); + } + + private static byte[] ToBytes(int value) + { + byte[] bytes = new byte[4]; + + bytes[3] = (byte)value; + bytes[2] = (byte)(value >> 8); + bytes[1] = (byte)(value >> 16); + bytes[0] = (byte)(value >> 24); + return bytes; + } + + public byte[] ToArray() + { + return _mBuf.ToArray(); + } + + /// + /// 从当前位置到结尾的字节数组的字符串表示 + /// + /// + public override string ToString() + { + byte[] data = _mBuf.ToArray(); + int offset = (int)_mBuf.Position; + string str = Encoding.UTF8.GetString(data, offset, data.Length - offset); + + //ToArray本身就不为该Position,所以下一行代码多余 + //_mBuf.Seek(offset, SeekOrigin.Begin); + return str; + } + } } \ No newline at end of file diff --git a/Message/Spi/Codec/IMessageCodec.cs b/Message/Spi/Codec/IMessageCodec.cs index 64daa0b..f738695 100644 --- a/Message/Spi/Codec/IMessageCodec.cs +++ b/Message/Spi/Codec/IMessageCodec.cs @@ -1,11 +1,11 @@ -namespace Com.Dianping.Cat.Message.Spi.Codec -{ - public interface IMessageCodec - { - IMessageTree Decode(ChannelBuffer buf); - - void Decode(ChannelBuffer buf, IMessageTree tree); - - void Encode(IMessageTree tree, ChannelBuffer buf); - } +namespace Com.Dianping.Cat.Message.Spi.Codec +{ + public interface IMessageCodec + { + IMessageTree Decode(ChannelBuffer buf); + + void Decode(ChannelBuffer buf, IMessageTree tree); + + void Encode(IMessageTree tree, ChannelBuffer buf); + } } \ No newline at end of file diff --git a/Message/Spi/Codec/PlainTextMessageCodec.cs b/Message/Spi/Codec/PlainTextMessageCodec.cs index e9fc980..a8be51f 100644 --- a/Message/Spi/Codec/PlainTextMessageCodec.cs +++ b/Message/Spi/Codec/PlainTextMessageCodec.cs @@ -1,526 +1,526 @@ -using Com.Dianping.Cat.Message.Internals; -using Com.Dianping.Cat.Util; -using System; -using System.Text; -using System.Globalization; -using System.Collections.Generic; - -namespace Com.Dianping.Cat.Message.Spi.Codec -{ - public class PlainTextMessageCodec : IMessageCodec - { - #region Policy enum - - public enum Policy - { - DEFAULT, - - WITHOUT_STATUS, - - WITH_DURATION - } - - #endregion - - private const String ID = "PT1"; // plain text version 1 - - private const byte TAB = (byte) '\t'; // tab character - - private const byte LF = (byte) '\n'; // line feed character - - private readonly BufferHelper _mBufferHelper; - - private readonly DateHelper _mDateHelper; - - public PlainTextMessageCodec() - { - _mBufferHelper = new BufferHelper(); - _mDateHelper = new DateHelper(); - } - - #region IMessageCodec Members - - public virtual IMessageTree Decode(ChannelBuffer buf) - { - DefaultMessageTree tree = new DefaultMessageTree(); - - Decode(buf, tree); - return tree; - } - - public virtual void Decode(ChannelBuffer buf, IMessageTree tree) - { - DecodeHeader(buf, tree); - - if (buf.ReadableBytes() > 0) - { - DecodeMessage(buf, tree); - } - } - - public virtual void Encode(IMessageTree tree, ChannelBuffer buf) - { - int count = 0; - - buf.WriteInt(0); // place-holder - count += EncodeHeader(tree, buf); - - if (tree.Message != null) - { - count += EncodeMessage(tree.Message, buf); - } - - buf.SetInt(0, count); - } - - #endregion - - protected internal void DecodeHeader(ChannelBuffer buf, IMessageTree tree) - { - BufferHelper helper = _mBufferHelper; - String id = helper.Read(buf, TAB); - String domain = helper.Read(buf, TAB); - String hostName = helper.Read(buf, TAB); - String ipAddress = helper.Read(buf, TAB); - String threadGroupName = helper.Read(buf, TAB); - String threadId = helper.Read(buf, TAB); - String threadName = helper.Read(buf, TAB); - String messageId = helper.Read(buf, TAB); - String parentMessageId = helper.Read(buf, TAB); - String rootMessageId = helper.Read(buf, TAB); - String sessionToken = helper.Read(buf, LF); - - if (ID.Equals(id)) - { - tree.Domain = domain; - tree.HostName = hostName; - tree.IpAddress = ipAddress; - tree.ThreadGroupName = threadGroupName; - tree.ThreadId = threadId; - tree.ThreadName = threadName; - tree.MessageId = messageId; - tree.ParentMessageId = parentMessageId; - tree.RootMessageId = rootMessageId; - tree.SessionToken = sessionToken; - } - else - { - throw new Exception("Unrecognized id(" + id + ") for plain text message codec!"); - } - } - - protected internal IMessage DecodeLine(ChannelBuffer buf, ITransaction parent, - Stack stack, IMessageTree tree) - { - BufferHelper helper = _mBufferHelper; - byte identifier = buf.ReadByte(); - String timestamp = helper.Read(buf, TAB); - String type = helper.Read(buf, TAB); - String name = helper.Read(buf, TAB); - - if (identifier == 'E') - { - IMessage evt = new DefaultEvent(type, name); - String status = helper.Read(buf, TAB); - String data = helper.ReadRaw(buf, TAB); - - helper.Read(buf, LF); // get rid of line feed - evt.Timestamp = _mDateHelper.Parse(timestamp); - evt.Status = status; - evt.AddData(data); - - if (parent != null) - { - parent.AddChild(evt); - return parent; - } - return evt; - } - if (identifier == 'H') - { - IMessage heartbeat = new DefaultHeartbeat(type, name); - String status0 = helper.Read(buf, TAB); - String data1 = helper.ReadRaw(buf, TAB); - - helper.Read(buf, LF); // get rid of line feed - heartbeat.Timestamp = _mDateHelper.Parse(timestamp); - heartbeat.Status = status0; - heartbeat.AddData(data1); - - if (parent != null) - { - parent.AddChild(heartbeat); - return parent; - } - return heartbeat; - } - if (identifier == 't') - { - IMessage transaction = new DefaultTransaction(type, name, - null); - - helper.Read(buf, LF); // get rid of line feed - transaction.Timestamp = _mDateHelper.Parse(timestamp); - - if (parent != null) - { - parent.AddChild(transaction); - } - - stack.Push(parent); - return transaction; - } - if (identifier == 'A') - { - ITransaction transaction2 = new DefaultTransaction(type, name, null); - String status3 = helper.Read(buf, TAB); - String duration = helper.Read(buf, TAB); - String data4 = helper.ReadRaw(buf, TAB); - - helper.Read(buf, LF); // get rid of line feed - transaction2.Timestamp = _mDateHelper.Parse(timestamp); - transaction2.Status = status3; - transaction2.AddData(data4); - - long d = Int64.Parse(duration.Substring(0, duration.Length - 2), NumberStyles.Integer); - transaction2.DurationInMicros = d; - - if (parent != null) - { - parent.AddChild(transaction2); - return parent; - } - return transaction2; - } - if (identifier == 'T') - { - String status5 = helper.Read(buf, TAB); - String duration6 = helper.Read(buf, TAB); - String data7 = helper.ReadRaw(buf, TAB); - - helper.Read(buf, LF); // get rid of line feed - parent.Status = status5; - parent.AddData(data7); - - long d8 = Int64.Parse( - duration6.Substring(0, duration6.Length - 2), - NumberStyles.Integer); - parent.DurationInMicros = d8; - - return stack.Pop(); - } - Logger.Error("Unknown identifier(" + identifier + ") of message: " + buf); - - // unknown message, ignore it - return parent; - } - - protected internal void DecodeMessage(ChannelBuffer buf, IMessageTree tree) - { - Stack stack = new Stack(); - IMessage parent = DecodeLine(buf, null, stack, tree); - - tree.Message = parent; - - while (buf.ReadableBytes() > 0) - { - IMessage message = DecodeLine(buf, (ITransaction) parent, stack, tree); - - if (message is ITransaction) - { - parent = message; - } - else - { - break; - } - } - } - - protected internal int EncodeHeader(IMessageTree tree, ChannelBuffer buf) - { - BufferHelper helper = _mBufferHelper; - int count = 0; - - count += helper.Write(buf, ID); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.Domain); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.HostName); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.IpAddress); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.ThreadGroupName); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.ThreadId); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.ThreadName); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.MessageId); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.ParentMessageId); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.RootMessageId); - count += helper.Write(buf, TAB); - count += helper.Write(buf, tree.SessionToken); - count += helper.Write(buf, LF); - - return count; - } - - protected internal int EncodeLine(IMessage message, ChannelBuffer buf, char type, Policy policy) - { - BufferHelper helper = _mBufferHelper; - int count = 0; - - count += helper.Write(buf, (byte) type); - - if (type == 'T' && message is ITransaction) - { - long duration = ((ITransaction) message).DurationInMillis; - - count += helper.Write(buf, _mDateHelper.Format(message.Timestamp + duration)); - } - else - { - count += helper.Write(buf, _mDateHelper.Format(message.Timestamp)); - } - - count += helper.Write(buf, TAB); - count += helper.Write(buf, message.Type); - count += helper.Write(buf, TAB); - count += helper.Write(buf, message.Name); - count += helper.Write(buf, TAB); - - if (policy != Policy.WITHOUT_STATUS) - { - count += helper.Write(buf, message.Status); - count += helper.Write(buf, TAB); - - Object data = message.Data; - - if (policy == Policy.WITH_DURATION && message is ITransaction) - { - long duration0 = ((ITransaction) message).DurationInMicros; - - count += helper.Write(buf, duration0.ToString(CultureInfo.InvariantCulture)); - //以微秒为单位 - count += helper.Write(buf, "us"); - count += helper.Write(buf, TAB); - } - - count += helper.WriteRaw(buf, data.ToString()); - count += helper.Write(buf, TAB); - } - - count += helper.Write(buf, LF); - - return count; - } - - public int EncodeMessage(IMessage message, ChannelBuffer buf) - { - if (message is IEvent) - { - return EncodeLine(message, buf, 'E', Policy.DEFAULT); - } - var transaction = message as ITransaction; - if (transaction != null) - { - IList children = transaction.Children; - - if ((children.Count == 0)) - { - return EncodeLine(transaction, buf, 'A', Policy.WITH_DURATION); - } - int count = 0; - int len = children.Count; - - count += EncodeLine(transaction, buf, 't', Policy.WITHOUT_STATUS); - - for (int i = 0; i < len; i++) - { - IMessage child = children[i]; - - count += EncodeMessage(child, buf); - } - - count += EncodeLine(transaction, buf, 'T', Policy.WITH_DURATION); - - return count; - } - if (message is IHeartbeat) - { - return EncodeLine(message, buf, 'H', Policy.DEFAULT); - } - throw new Exception("Unsupported message type: " + message.Type + "."); - } - - #region Nested type: BufferHelper - - protected internal class BufferHelper - { - private readonly UTF8Encoding _mEncoding = new UTF8Encoding(); - - public String Read(ChannelBuffer buf, byte separator) - { - int count = buf.BytesBefore(separator); - - if (count < 0) - { - return null; - } - byte[] data = new byte[count]; - - buf.ReadBytes(data); - buf.ReadByte(); // get rid of separator - - return Encoding.UTF8.GetString(data); - } - - public String ReadRaw(ChannelBuffer buf, byte separator) - { - int count = buf.BytesBefore(separator); - - if (count < 0) - { - return null; - } - byte[] data = new byte[count]; - - buf.ReadBytes(data); - buf.ReadByte(); // get rid of separator - - int length = data.Length; - - for (int i = 0; i < length; i++) - { - if (data[i] == '\\') - { - if (i + 1 < length) - { - byte b = data[i + 1]; - - if (b == 't') - { - data[i] = (byte) '\t'; - } - else if (b == 'r') - { - data[i] = (byte) '\r'; - } - else if (b == 'n') - { - data[i] = (byte) '\n'; - } - else - { - data[i] = b; - } - - Array.Copy(data, i + 2, data, i + 1, length - i - 2); - length--; - } - } - } - - return Encoding.UTF8.GetString(data, 0, length); - } - - public int Write(ChannelBuffer buf, byte b) - { - buf.WriteByte(b); - return 1; - } - - public int Write(ChannelBuffer buf, String str) - { - if (str == null) - { - str = "null"; - } - - byte[] data = _mEncoding.GetBytes(str); - - buf.WriteBytes(data); - return data.Length; - } - - public int WriteRaw(ChannelBuffer buf, String str) - { - if (str == null) - { - str = "null"; - } - - byte[] data = _mEncoding.GetBytes(str); - - int len = data.Length; - int count = len; - int offset = 0; - - for (int i = 0; i < len; i++) - { - byte b = data[i]; - - if (b == '\t' || b == '\r' || b == '\n' || b == '\\') - { - buf.WriteBytes(data, offset, i - offset); - buf.WriteByte('\\'); - - if (b == '\t') - { - buf.WriteByte('t'); - } - else if (b == '\r') - { - buf.WriteByte('r'); - } - else if (b == '\n') - { - buf.WriteByte('n'); - } - else - { - buf.WriteByte(b); - } - - count++; - offset = i + 1; - } - } - - if (len > offset) - { - buf.WriteBytes(data, offset, len - offset); - } - - return count; - } - } - - #endregion - - #region Nested type: DateHelper - - /// - /// Thread safe date helper class. DateFormat is NOT thread safe. - /// - protected internal class DateHelper - { - public String Format(long timestamp) - { - return new DateTime(timestamp*10000L).ToString("yyyy-MM-dd HH:mm:ss.fff"); - } - - public long Parse(String str) - { - DateTime dateTime = DateTime.ParseExact(str, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.CurrentCulture); - - return dateTime.Ticks/10000L; - } - } - - #endregion - } +using Com.Dianping.Cat.Message.Internals; +using Com.Dianping.Cat.Util; +using System; +using System.Text; +using System.Globalization; +using System.Collections.Generic; + +namespace Com.Dianping.Cat.Message.Spi.Codec +{ + public class PlainTextMessageCodec : IMessageCodec + { + #region Policy enum + + public enum Policy + { + DEFAULT, + + WITHOUT_STATUS, + + WITH_DURATION + } + + #endregion + + private const String ID = "PT1"; // plain text version 1 + + private const byte TAB = (byte)'\t'; // tab character + + private const byte LF = (byte)'\n'; // line feed character + + private readonly BufferHelper _mBufferHelper; + + private readonly DateHelper _mDateHelper; + + public PlainTextMessageCodec() + { + _mBufferHelper = new BufferHelper(); + _mDateHelper = new DateHelper(); + } + + #region IMessageCodec Members + + public virtual IMessageTree Decode(ChannelBuffer buf) + { + DefaultMessageTree tree = new DefaultMessageTree(); + + Decode(buf, tree); + return tree; + } + + public virtual void Decode(ChannelBuffer buf, IMessageTree tree) + { + DecodeHeader(buf, tree); + + if (buf.ReadableBytes() > 0) + { + DecodeMessage(buf, tree); + } + } + + public virtual void Encode(IMessageTree tree, ChannelBuffer buf) + { + int count = 0; + + buf.WriteInt(0); // place-holder + count += EncodeHeader(tree, buf); + + if (tree.Message != null) + { + count += EncodeMessage(tree.Message, buf); + } + + buf.SetInt(0, count); + } + + #endregion + + protected internal void DecodeHeader(ChannelBuffer buf, IMessageTree tree) + { + BufferHelper helper = _mBufferHelper; + String id = helper.Read(buf, TAB); + String domain = helper.Read(buf, TAB); + String hostName = helper.Read(buf, TAB); + String ipAddress = helper.Read(buf, TAB); + String threadGroupName = helper.Read(buf, TAB); + String threadId = helper.Read(buf, TAB); + String threadName = helper.Read(buf, TAB); + String messageId = helper.Read(buf, TAB); + String parentMessageId = helper.Read(buf, TAB); + String rootMessageId = helper.Read(buf, TAB); + String sessionToken = helper.Read(buf, LF); + + if (ID.Equals(id)) + { + tree.Domain = domain; + tree.HostName = hostName; + tree.IpAddress = ipAddress; + tree.ThreadGroupName = threadGroupName; + tree.ThreadId = threadId; + tree.ThreadName = threadName; + tree.MessageId = messageId; + tree.ParentMessageId = parentMessageId; + tree.RootMessageId = rootMessageId; + tree.SessionToken = sessionToken; + } + else + { + throw new Exception("Unrecognized id(" + id + ") for plain text message codec!"); + } + } + + protected internal IMessage DecodeLine(ChannelBuffer buf, ITransaction parent, + Stack stack, IMessageTree tree) + { + BufferHelper helper = _mBufferHelper; + byte identifier = buf.ReadByte(); + String timestamp = helper.Read(buf, TAB); + String type = helper.Read(buf, TAB); + String name = helper.Read(buf, TAB); + + if (identifier == 'E') + { + IMessage evt = new DefaultEvent(type, name); + String status = helper.Read(buf, TAB); + String data = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); // get rid of line feed + evt.Timestamp = _mDateHelper.Parse(timestamp); + evt.Status = status; + evt.AddData(data); + + if (parent != null) + { + parent.AddChild(evt); + return parent; + } + return evt; + } + if (identifier == 'H') + { + IMessage heartbeat = new DefaultHeartbeat(type, name); + String status0 = helper.Read(buf, TAB); + String data1 = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); // get rid of line feed + heartbeat.Timestamp = _mDateHelper.Parse(timestamp); + heartbeat.Status = status0; + heartbeat.AddData(data1); + + if (parent != null) + { + parent.AddChild(heartbeat); + return parent; + } + return heartbeat; + } + if (identifier == 't') + { + IMessage transaction = new DefaultTransaction(type, name, + null); + + helper.Read(buf, LF); // get rid of line feed + transaction.Timestamp = _mDateHelper.Parse(timestamp); + + if (parent != null) + { + parent.AddChild(transaction); + } + + stack.Push(parent); + return transaction; + } + if (identifier == 'A') + { + ITransaction transaction2 = new DefaultTransaction(type, name, null); + String status3 = helper.Read(buf, TAB); + String duration = helper.Read(buf, TAB); + String data4 = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); // get rid of line feed + transaction2.Timestamp = _mDateHelper.Parse(timestamp); + transaction2.Status = status3; + transaction2.AddData(data4); + + long d = Int64.Parse(duration.Substring(0, duration.Length - 2), NumberStyles.Integer); + transaction2.DurationInMicros = d; + + if (parent != null) + { + parent.AddChild(transaction2); + return parent; + } + return transaction2; + } + if (identifier == 'T') + { + String status5 = helper.Read(buf, TAB); + String duration6 = helper.Read(buf, TAB); + String data7 = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); // get rid of line feed + parent.Status = status5; + parent.AddData(data7); + + long d8 = Int64.Parse( + duration6.Substring(0, duration6.Length - 2), + NumberStyles.Integer); + parent.DurationInMicros = d8; + + return stack.Pop(); + } + Logger.Error("Unknown identifier(" + identifier + ") of message: " + buf); + + // unknown message, ignore it + return parent; + } + + protected internal void DecodeMessage(ChannelBuffer buf, IMessageTree tree) + { + Stack stack = new Stack(); + IMessage parent = DecodeLine(buf, null, stack, tree); + + tree.Message = parent; + + while (buf.ReadableBytes() > 0) + { + IMessage message = DecodeLine(buf, (ITransaction)parent, stack, tree); + + if (message is ITransaction) + { + parent = message; + } + else + { + break; + } + } + } + + protected internal int EncodeHeader(IMessageTree tree, ChannelBuffer buf) + { + BufferHelper helper = _mBufferHelper; + int count = 0; + + count += helper.Write(buf, ID); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.Domain); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.HostName); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.IpAddress); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.ThreadGroupName); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.ThreadId); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.ThreadName); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.MessageId); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.ParentMessageId); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.RootMessageId); + count += helper.Write(buf, TAB); + count += helper.Write(buf, tree.SessionToken); + count += helper.Write(buf, LF); + + return count; + } + + protected internal int EncodeLine(IMessage message, ChannelBuffer buf, char type, Policy policy) + { + BufferHelper helper = _mBufferHelper; + int count = 0; + + count += helper.Write(buf, (byte)type); + + if (type == 'T' && message is ITransaction) + { + long duration = ((ITransaction)message).DurationInMillis; + + count += helper.Write(buf, _mDateHelper.Format(message.Timestamp + duration)); + } + else + { + count += helper.Write(buf, _mDateHelper.Format(message.Timestamp)); + } + + count += helper.Write(buf, TAB); + count += helper.Write(buf, message.Type); + count += helper.Write(buf, TAB); + count += helper.Write(buf, message.Name); + count += helper.Write(buf, TAB); + + if (policy != Policy.WITHOUT_STATUS) + { + count += helper.Write(buf, message.Status); + count += helper.Write(buf, TAB); + + Object data = message.Data; + + if (policy == Policy.WITH_DURATION && message is ITransaction) + { + long duration0 = ((ITransaction)message).DurationInMicros; + + count += helper.Write(buf, duration0.ToString(CultureInfo.InvariantCulture)); + //以微秒为单位 + count += helper.Write(buf, "us"); + count += helper.Write(buf, TAB); + } + + count += helper.WriteRaw(buf, data.ToString()); + count += helper.Write(buf, TAB); + } + + count += helper.Write(buf, LF); + + return count; + } + + public int EncodeMessage(IMessage message, ChannelBuffer buf) + { + if (message is IEvent) + { + return EncodeLine(message, buf, 'E', Policy.DEFAULT); + } + var transaction = message as ITransaction; + if (transaction != null) + { + IList children = transaction.Children; + + if ((children.Count == 0)) + { + return EncodeLine(transaction, buf, 'A', Policy.WITH_DURATION); + } + int count = 0; + int len = children.Count; + + count += EncodeLine(transaction, buf, 't', Policy.WITHOUT_STATUS); + + for (int i = 0; i < len; i++) + { + IMessage child = children[i]; + + count += EncodeMessage(child, buf); + } + + count += EncodeLine(transaction, buf, 'T', Policy.WITH_DURATION); + + return count; + } + if (message is IHeartbeat) + { + return EncodeLine(message, buf, 'H', Policy.DEFAULT); + } + throw new Exception("Unsupported message type: " + message.Type + "."); + } + + #region Nested type: BufferHelper + + protected internal class BufferHelper + { + private readonly UTF8Encoding _mEncoding = new UTF8Encoding(); + + public String Read(ChannelBuffer buf, byte separator) + { + int count = buf.BytesBefore(separator); + + if (count < 0) + { + return null; + } + byte[] data = new byte[count]; + + buf.ReadBytes(data); + buf.ReadByte(); // get rid of separator + + return Encoding.UTF8.GetString(data); + } + + public String ReadRaw(ChannelBuffer buf, byte separator) + { + int count = buf.BytesBefore(separator); + + if (count < 0) + { + return null; + } + byte[] data = new byte[count]; + + buf.ReadBytes(data); + buf.ReadByte(); // get rid of separator + + int length = data.Length; + + for (int i = 0; i < length; i++) + { + if (data[i] == '\\') + { + if (i + 1 < length) + { + byte b = data[i + 1]; + + if (b == 't') + { + data[i] = (byte)'\t'; + } + else if (b == 'r') + { + data[i] = (byte)'\r'; + } + else if (b == 'n') + { + data[i] = (byte)'\n'; + } + else + { + data[i] = b; + } + + Array.Copy(data, i + 2, data, i + 1, length - i - 2); + length--; + } + } + } + + return Encoding.UTF8.GetString(data, 0, length); + } + + public int Write(ChannelBuffer buf, byte b) + { + buf.WriteByte(b); + return 1; + } + + public int Write(ChannelBuffer buf, String str) + { + if (str == null) + { + str = "null"; + } + + byte[] data = _mEncoding.GetBytes(str); + + buf.WriteBytes(data); + return data.Length; + } + + public int WriteRaw(ChannelBuffer buf, String str) + { + if (str == null) + { + str = "null"; + } + + byte[] data = _mEncoding.GetBytes(str); + + int len = data.Length; + int count = len; + int offset = 0; + + for (int i = 0; i < len; i++) + { + byte b = data[i]; + + if (b == '\t' || b == '\r' || b == '\n' || b == '\\') + { + buf.WriteBytes(data, offset, i - offset); + buf.WriteByte('\\'); + + if (b == '\t') + { + buf.WriteByte('t'); + } + else if (b == '\r') + { + buf.WriteByte('r'); + } + else if (b == '\n') + { + buf.WriteByte('n'); + } + else + { + buf.WriteByte(b); + } + + count++; + offset = i + 1; + } + } + + if (len > offset) + { + buf.WriteBytes(data, offset, len - offset); + } + + return count; + } + } + + #endregion + + #region Nested type: DateHelper + + /// + /// Thread safe date helper class. DateFormat is NOT thread safe. + /// + protected internal class DateHelper + { + public String Format(long timestamp) + { + return new DateTime(timestamp * 10000L).ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + + public long Parse(String str) + { + DateTime dateTime = DateTime.ParseExact(str, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.CurrentCulture); + + return dateTime.Ticks / 10000L; + } + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Spi/IMessageManager.cs b/Message/Spi/IMessageManager.cs index 70a4d1d..3e39120 100644 --- a/Message/Spi/IMessageManager.cs +++ b/Message/Spi/IMessageManager.cs @@ -1,84 +1,80 @@ using Com.Dianping.Cat.Configuration; -using Com.Dianping.Cat.Message.Spi.Internals; - -namespace Com.Dianping.Cat.Message.Spi -{ - /// - /// Message manager to help build CAT message.

    Notes: This method is reserved for internal usage only. Application developer - /// should never call this method directly.

    - ///
    - public interface IMessageManager - { - /// - /// Return configuration for CAT client. - /// - /// CAT configuration - ClientConfig ClientConfig { get; } - - //TransportManager TransportManager { get; } - - /// - /// Get peek transaction for current thread. - /// - /// peek transaction for current thread, null if no transaction there. - ITransaction PeekTransaction { get; } - - /// - /// Get thread local message information. - /// - /// message tree, null means current thread is not setup correctly. - IMessageTree ThreadLocalMessageTree { get; } - - /// - /// Check if CAT logging is enabled or disabled. - /// - /// true if CAT is enabled - bool CatEnabled { get; } - - /// - /// 用于添加Event或者Heartbeat到peek transaction或者到根 如果是添加到根,建议直接使用IMessageProducer中的LogError、LogEvent或LogHeartbeat方法 - /// - /// - void Add(IMessage message); - - /// - /// Initialize CAT client with given CAT configuration. - /// - /// CAT configuration - void InitializeClient(ClientConfig config); - - /// - /// Do cleanup for current thread environment in order to release resources in thread local objects. - /// - void Reset(); - - /// - /// Check if the thread context is setup or not. - /// - /// true if the thread context is setup, false otherwise - bool HasContext(); - - /// - /// Do setup for current thread environment in order to prepare thread local objects. - /// - void Setup(); - - /// - /// Be triggered when a new transaction starts, whatever it's the root transaction or nested transaction. - /// - /// - void Start(ITransaction transaction); - - /// - /// Be triggered when a transaction ends, whatever it's the root transaction or nested transaction. However, if it's the root transaction then it will be flushed to back-end CAT server asynchronously. - /// - /// - void End(ITransaction transaction); +using Com.Dianping.Cat.Message.Spi.Internals; + +namespace Com.Dianping.Cat.Message.Spi +{ + /// + /// Message manager to help build CAT message.

    Notes: This method is reserved for internal usage only. Application developer + /// should never call this method directly.

    + ///
    + public interface IMessageManager + { + /// + /// Return configuration for CAT client. + /// + /// CAT configuration + ClientConfig ClientConfig { get; } + + //TransportManager TransportManager { get; } + + /// + /// Get peek transaction for current thread. + /// + /// peek transaction for current thread, null if no transaction there. + ITransaction PeekTransaction { get; } + + /// + /// Get thread local message information. + /// + /// message tree, null means current thread is not setup correctly. + IMessageTree ThreadLocalMessageTree { get; } + + /// + /// Check if CAT logging is enabled or disabled. + /// + /// true if CAT is enabled + bool CatEnabled { get; } /// - /// get MessageIdFactory + /// 用于添加Event或者Heartbeat到peek transaction或者到根 如果是添加到根,建议直接使用IMessageProducer中的LogError、LogEvent或LogHeartbeat方法 /// - /// - MessageIdFactory GetMessageIdFactory(); - } + /// + void Add(IMessage message); + + /// + /// Initialize CAT client with given CAT configuration. + /// + /// CAT configuration + void InitializeClient(ClientConfig config); + + /// + /// Do cleanup for current thread environment in order to release resources in thread local objects. + /// + void Reset(); + + /// + /// Check if the thread context is setup or not. + /// + /// true if the thread context is setup, false otherwise + bool HasContext(); + + /// + /// Do setup for current thread environment in order to prepare thread local objects. + /// + void Setup(); + + /// + /// Be triggered when a new transaction starts, whatever it's the root transaction or nested transaction. + /// + /// + void Start(ITransaction transaction); + + /// + /// Be triggered when a transaction ends, whatever it's the root transaction or nested transaction. However, if it's the root transaction then it will be flushed to back-end CAT server asynchronously. + /// + /// + void End(ITransaction transaction); + + MessageIdFactory GetMessageIdFactory(); + } } \ No newline at end of file diff --git a/Message/Spi/IMessageProducer.cs b/Message/Spi/IMessageProducer.cs index 3fc9259..c7152d6 100644 --- a/Message/Spi/IMessageProducer.cs +++ b/Message/Spi/IMessageProducer.cs @@ -1,137 +1,133 @@ -using System; - -namespace Com.Dianping.Cat.Message.Spi -{ - /// - ///

    Message factory is used to create new transaction,event and/or heartbeat.

    Normally, application code logs message in following ways, for example: - ///

      - ///
    • Event - ///
      public class MyClass { 
      -    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      -    ///                                                                                           public void bizMethod() { 
      -    ///                                                                                           Event event = CAT.newEvent("Review", "New");
      -    ///                                                                                           event.addData("id", 12345); 
      -    ///                                                                                           event.addData("user", "john");
      -    ///                                                                                           ...
      -    ///                                                                                           event.setStatus("0"); 
      -    ///                                                                                           event.complete(); 
      -    ///                                                                                           }
      -    ///                                                                                           ...
      -    ///                                                                                           }
      - ///
    • - ///
    • Heartbeat - ///
      public class MyClass { 
      -    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      -    ///                                                                                           public void bizMethod() { 
      -    ///                                                                                           Heartbeat event = CAT.newHeartbeat("System", "Status");
      -    ///                                                                                           event.addData("ip", "192.168.10.111");
      -    ///                                                                                           event.addData("host", "host-1");
      -    ///                                                                                           event.addData("load", "2.1");
      -    ///                                                                                           event.addData("cpu", "0.12,0.10");
      -    ///                                                                                           event.addData("memory.total", "2G");
      -    ///                                                                                           event.addData("memory.free", "456M");
      -    ///                                                                                           event.setStatus("0");
      -    ///                                                                                           event.complete();
      -    ///                                                                                           }
      -    ///                                                                                           ...
      -    ///                                                                                           }
      - ///
    • - ///
    • Transaction - ///
      public class MyClass { 
      -    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      -    ///                                                                                           public void bizMethod() { 
      -    ///                                                                                           Transaction t = CAT.newTransaction("URL", "MyPage");
      -    ///                                                                                           try {
      -    ///                                                                                           // do your business here
      -    ///                                                                                           t.addData("k1", "v1");
      -    ///                                                                                           t.addData("k2", "v2");
      -    ///                                                                                           t.addData("k3", "v3");
      -    ///                                                                                           Thread.sleep(30);
      -    ///                                                                                           t.setStatus("0");
      -    ///                                                                                           } catch (Exception e) {
      -    ///                                                                                           t.setStatus(e);
      -    ///                                                                                           } finally {
      -    ///                                                                                           t.complete();
      -    ///                                                                                           }
      -    ///                                                                                           }
      -    ///                                                                                           ...
      -    ///                                                                                           }
      - ///
    • - ///
    - /// or logs event or heartbeat in one shot, for example: - ///
      - ///
    • Event - ///
      public class MyClass { 
      -    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      -    ///                                                                                           public void bizMethod() { 
      -    ///                                                                                           CAT.logEvent("Review", "New", "0", "id=12345&user=john");
      -    ///                                                                                           }
      -    ///                                                                                           ...
      -    ///                                                                                           }
      - ///
    • - ///
    • Heartbeat - ///
      public class MyClass { 
      -    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      -    ///                                                                                           public void bizMethod() { 
      -    ///                                                                                           CAT.logHeartbeat("System", "Status", "0", "ip=192.168.10.111&host=host-1&load=2.1&cpu=0.12,0.10&memory.total=2G&memory.free=456M");
      -    ///                                                                                           }
      -    ///                                                                                           ...
      -    ///                                                                                           }
      - ///
    • - ///
    - ///

    - ///
    - public interface IMessageProducer - { - /// - /// Log an error. - /// - /// root cause exception - void LogError(Exception cause); - - /// - /// Log an event in one shot. - /// - /// event type - /// event name - /// "0" means success, otherwise means error code - /// name value pairs in the format of "a=1&b=2&..." - void LogEvent(String type, String name, String status, String nameValuePairs); - - /// - /// Log a heartbeat in one shot. - /// - /// heartbeat type - /// heartbeat name - /// "0" means success, otherwise means error code - /// name value pairs in the format of "a=1&b=2&..." - void LogHeartbeat(String type, String name, String status, String nameValuePairs); - - /// - /// Create a new event with given type and name. - /// - /// event type - /// event name - IEvent NewEvent(String type, String name); - - /// - /// Create a new heartbeat with given type and name. - /// - /// heartbeat type - /// heartbeat name - IHeartbeat NewHeartbeat(String type, String name); - - /// - /// Create a new transaction with given type and name. - /// - /// transaction type - /// transaction name +using System; + +namespace Com.Dianping.Cat.Message.Spi +{ + /// + ///

    Message factory is used to create new transaction,event and/or heartbeat.

    Normally, application code logs message in following ways, for example: + ///

      + ///
    • Event + ///
      public class MyClass { 
      +    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      +    ///                                                                                           public void bizMethod() { 
      +    ///                                                                                           Event event = CAT.newEvent("Review", "New");
      +    ///                                                                                           event.addData("id", 12345); 
      +    ///                                                                                           event.addData("user", "john");
      +    ///                                                                                           ...
      +    ///                                                                                           event.setStatus("0"); 
      +    ///                                                                                           event.complete(); 
      +    ///                                                                                           }
      +    ///                                                                                           ...
      +    ///                                                                                           }
      + ///
    • + ///
    • Heartbeat + ///
      public class MyClass { 
      +    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      +    ///                                                                                           public void bizMethod() { 
      +    ///                                                                                           Heartbeat event = CAT.newHeartbeat("System", "Status");
      +    ///                                                                                           event.addData("ip", "192.168.10.111");
      +    ///                                                                                           event.addData("host", "host-1");
      +    ///                                                                                           event.addData("load", "2.1");
      +    ///                                                                                           event.addData("cpu", "0.12,0.10");
      +    ///                                                                                           event.addData("memory.total", "2G");
      +    ///                                                                                           event.addData("memory.free", "456M");
      +    ///                                                                                           event.setStatus("0");
      +    ///                                                                                           event.complete();
      +    ///                                                                                           }
      +    ///                                                                                           ...
      +    ///                                                                                           }
      + ///
    • + ///
    • Transaction + ///
      public class MyClass { 
      +    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      +    ///                                                                                           public void bizMethod() { 
      +    ///                                                                                           Transaction t = CAT.newTransaction("URL", "MyPage");
      +    ///                                                                                           try {
      +    ///                                                                                           // do your business here
      +    ///                                                                                           t.addData("k1", "v1");
      +    ///                                                                                           t.addData("k2", "v2");
      +    ///                                                                                           t.addData("k3", "v3");
      +    ///                                                                                           Thread.sleep(30);
      +    ///                                                                                           t.setStatus("0");
      +    ///                                                                                           } catch (Exception e) {
      +    ///                                                                                           t.setStatus(e);
      +    ///                                                                                           } finally {
      +    ///                                                                                           t.complete();
      +    ///                                                                                           }
      +    ///                                                                                           }
      +    ///                                                                                           ...
      +    ///                                                                                           }
      + ///
    • + ///
    + /// or logs event or heartbeat in one shot, for example: + ///
      + ///
    • Event + ///
      public class MyClass { 
      +    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      +    ///                                                                                           public void bizMethod() { 
      +    ///                                                                                           CAT.logEvent("Review", "New", "0", "id=12345&user=john");
      +    ///                                                                                           }
      +    ///                                                                                           ...
      +    ///                                                                                           }
      + ///
    • + ///
    • Heartbeat + ///
      public class MyClass { 
      +    ///                                                                                           public static MessageFactory CAT = Cat.getFactory();
      +    ///                                                                                           public void bizMethod() { 
      +    ///                                                                                           CAT.logHeartbeat("System", "Status", "0", "ip=192.168.10.111&host=host-1&load=2.1&cpu=0.12,0.10&memory.total=2G&memory.free=456M");
      +    ///                                                                                           }
      +    ///                                                                                           ...
      +    ///                                                                                           }
      + ///
    • + ///
    + ///

    + ///
    + public interface IMessageProducer + { + /// + /// Log an error. + /// + /// root cause exception + void LogError(Exception cause); + + /// + /// Log an event in one shot. + /// + /// event type + /// event name + /// "0" means success, otherwise means error code + /// name value pairs in the format of "a=1&b=2&..." + void LogEvent(String type, String name, String status, String nameValuePairs); + + /// + /// Log a heartbeat in one shot. + /// + /// heartbeat type + /// heartbeat name + /// "0" means success, otherwise means error code + /// name value pairs in the format of "a=1&b=2&..." + void LogHeartbeat(String type, String name, String status, String nameValuePairs); + + /// + /// Create a new event with given type and name. + /// + /// event type + /// event name + IEvent NewEvent(String type, String name); + + /// + /// Create a new heartbeat with given type and name. + /// + /// heartbeat type + /// heartbeat name + IHeartbeat NewHeartbeat(String type, String name); + + /// + /// Create a new transaction with given type and name. + /// + /// transaction type + /// transaction name ITransaction NewTransaction(String type, String name); - /// - /// Create MessageId - /// - /// - String CreateMessageId(); - } + String CreateMessageId(); + } } \ No newline at end of file diff --git a/Message/Spi/IMessageStatistics.cs b/Message/Spi/IMessageStatistics.cs index e289cf0..c3a3fb0 100644 --- a/Message/Spi/IMessageStatistics.cs +++ b/Message/Spi/IMessageStatistics.cs @@ -1,17 +1,17 @@ -namespace Com.Dianping.Cat.Message.Spi -{ - public interface IMessageStatistics - { - long Produced { get; set; } - - long Overflowed { get; set; } - - long Bytes { get; set; } - - void OnSending(IMessageTree tree); - - void OnOverflowed(IMessageTree tree); - - void OnBytes(int size); - } +namespace Com.Dianping.Cat.Message.Spi +{ + public interface IMessageStatistics + { + long Produced { get; set; } + + long Overflowed { get; set; } + + long Bytes { get; set; } + + void OnSending(IMessageTree tree); + + void OnOverflowed(IMessageTree tree); + + void OnBytes(int size); + } } \ No newline at end of file diff --git a/Message/Spi/IO/IMessageSender.cs b/Message/Spi/IO/IMessageSender.cs index 7d1c881..09d8f65 100644 --- a/Message/Spi/IO/IMessageSender.cs +++ b/Message/Spi/IO/IMessageSender.cs @@ -1,13 +1,13 @@ -namespace Com.Dianping.Cat.Message.Spi.IO -{ - public interface IMessageSender - { - bool HasSendingMessage { get; } - - void Initialize(); - - void Send(IMessageTree tree); - - void Shutdown(); - } +namespace Com.Dianping.Cat.Message.Spi.IO +{ + public interface IMessageSender + { + bool HasSendingMessage { get; } + + void Initialize(); + + void Send(IMessageTree tree); + + void Shutdown(); + } } \ No newline at end of file diff --git a/Message/Spi/IO/TcpMessageSender.cs b/Message/Spi/IO/TcpMessageSender.cs index 5ee4edc..a64efae 100644 --- a/Message/Spi/IO/TcpMessageSender.cs +++ b/Message/Spi/IO/TcpMessageSender.cs @@ -1,263 +1,263 @@ -using System; -using Com.Dianping.Cat.Configuration; -using Com.Dianping.Cat.Message.Spi.Codec; -using Com.Dianping.Cat.Util; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Threading; - -namespace Com.Dianping.Cat.Message.Spi.IO -{ - public class TcpMessageSender : IMessageSender - { - private readonly ClientConfig _mClientConfig; - private readonly IMessageCodec _mCodec; - private readonly IList _mQueue; - private readonly IMessageStatistics _mStatistics; - private bool _mActive; - private TcpClient _mActiveChannel; - private int _mActiveIndex; - private int _mErrors; - private TcpClient _mLastChannel; - - public TcpMessageSender(ClientConfig clientConfig, IMessageStatistics statistics) - { - _mClientConfig = clientConfig; - _mStatistics = statistics; - _mActive = true; - _mQueue = new List(100000); - _mCodec = new PlainTextMessageCodec(); - } - - #region IMessageSender Members - - public virtual bool HasSendingMessage - { - get { return _mQueue.Count > 0; } - } - - public void Initialize() - { - int len = _mClientConfig.Servers.Count; - - for (int i = 0; i < len; i++) - { - TcpClient channel = CreateChannel(i); - - if (channel != null) - { - _mActiveChannel = channel; - _mActiveIndex = i; - break; - } - } - - ThreadPool.QueueUserWorkItem(ChannelManagementTask); - ThreadPool.QueueUserWorkItem(AsynchronousSendTask); - - Logger.Info("Thread(TcpMessageSender-ChannelManagementTask) started."); - Logger.Info("Thread(TcpMessageSender-AsynchronousSendTask) started."); - } - - public void Send(IMessageTree tree) - { - lock (_mQueue) - { - if (_mQueue.Count < 100000) - { - _mQueue.Add(tree); - } - else - { - // throw it away since the queue is full - _mErrors ++; - - if (_mStatistics != null) - { - _mStatistics.OnOverflowed(tree); - } - - if (_mErrors%100 == 0) - { - Logger.Warn("Can't send message to cat-server due to queue's full! Count: " + _mErrors); - } - } - } - } - - public void Shutdown() - { - _mActive = false; - - try - { - if (_mActiveChannel != null && _mActiveChannel.Connected) - { - _mActiveChannel.Close(); - } - } - catch - { - // ignore it - } - } - - #endregion - - public void ChannelManagementTask(object o) - { - while (true) - { - if (_mActive) - { - if (_mActiveChannel == null || !_mActiveChannel.Connected) - { - if (_mActiveChannel != null) - Logger.Warn("ChannelManagementTask中,Socket关闭"); - _mActiveIndex = _mClientConfig.Servers.Count; - } - - for (int i = 0; i < _mActiveIndex; i++) - { - TcpClient channel = CreateChannel(i); - - if (channel != null) - { - _mLastChannel = _mActiveChannel; - _mActiveChannel = channel; - _mActiveIndex = i; - break; - } - } - } - - Thread.Sleep(5*1000); // every 2 seconds - } - } - - public void AsynchronousSendTask(object o) - { - while (true) - { - if (_mActive) - { - while (_mQueue.Count == 0 || _mActiveChannel == null || !_mActiveChannel.Connected) - { - if (_mActiveChannel != null && !_mActiveChannel.Connected) - Logger.Warn("AsynchronousSendTask中,Socket关闭"); - Thread.Sleep(5*1000); - } - - IMessageTree tree = null; - - lock (_mQueue) - { - foreach (IMessageTree t in _mQueue) - { - tree = t; - break; - } - - _mQueue.RemoveAt(0); - } - - try - { - SendInternal(tree); - if (tree != null) tree.Message = null; - } - catch (Exception t) - { - Logger.Error("Error when sending message over TCP socket! Error: {0}", t); - } - } - else - { - Thread.Sleep(5*1000); - } - } - } - - private void SendInternal(IMessageTree tree) - { - if (_mLastChannel != null) - { - try - { - Logger.Warn("SendInternal中,_mLastChannel关闭"); - _mLastChannel.Close(); - } - catch - { - // ignore it - } - - _mLastChannel = null; - } - - if (_mActiveChannel != null && _mActiveChannel.Connected) - { - ChannelBuffer buf = new ChannelBuffer(8192); - - _mCodec.Encode(tree, buf); - - byte[] data = buf.ToArray(); - - _mActiveChannel.Client.Send(data); - - if (_mStatistics != null) - { - _mStatistics.OnBytes(data.Length); - } - } - else - { - Logger.Warn("SendInternal中,Socket关闭"); - } - } - - private TcpClient CreateChannel(int index) - { - Server server = _mClientConfig.Servers[index]; - - if (!server.Enabled) - { - return null; - } - - TcpClient socket = new TcpClient(); - - socket.NoDelay = true; - socket.ReceiveTimeout = 2*1000; // 2 seconds - - string ip = server.Ip; - int port = server.Port; - - Logger.Info("Connecting to server({0}:{1}) ...", ip, port); - - try - { - socket.Connect(ip, port); - - if (socket.Connected) - { - Logger.Info("Connected to server({0}:{1}).", ip, port); - - return socket; - } - Logger.Error("Failed to connect to server({0}:{1}).", ip, port); - } - catch (Exception e) - { - Logger.Error( - "Failed to connect to server({0}:{1}). Error: {2}.", - ip, - port, - e.Message - ); - } - - return null; - } - } +using System; +using Com.Dianping.Cat.Configuration; +using Com.Dianping.Cat.Message.Spi.Codec; +using Com.Dianping.Cat.Util; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Threading; + +namespace Com.Dianping.Cat.Message.Spi.IO +{ + public class TcpMessageSender : IMessageSender + { + private readonly ClientConfig _mClientConfig; + private readonly IMessageCodec _mCodec; + private readonly IList _mQueue; + private readonly IMessageStatistics _mStatistics; + private bool _mActive; + private TcpClient _mActiveChannel; + private int _mActiveIndex; + private int _mErrors; + private TcpClient _mLastChannel; + + public TcpMessageSender(ClientConfig clientConfig, IMessageStatistics statistics) + { + _mClientConfig = clientConfig; + _mStatistics = statistics; + _mActive = true; + _mQueue = new List(100000); + _mCodec = new PlainTextMessageCodec(); + } + + #region IMessageSender Members + + public virtual bool HasSendingMessage + { + get { return _mQueue.Count > 0; } + } + + public void Initialize() + { + int len = _mClientConfig.Servers.Count; + + for (int i = 0; i < len; i++) + { + TcpClient channel = CreateChannel(i); + + if (channel != null) + { + _mActiveChannel = channel; + _mActiveIndex = i; + break; + } + } + + ThreadPool.QueueUserWorkItem(ChannelManagementTask); + ThreadPool.QueueUserWorkItem(AsynchronousSendTask); + + Logger.Info("Thread(TcpMessageSender-ChannelManagementTask) started."); + Logger.Info("Thread(TcpMessageSender-AsynchronousSendTask) started."); + } + + public void Send(IMessageTree tree) + { + lock (_mQueue) + { + if (_mQueue.Count < 100000) + { + _mQueue.Add(tree); + } + else + { + // throw it away since the queue is full + _mErrors++; + + if (_mStatistics != null) + { + _mStatistics.OnOverflowed(tree); + } + + if (_mErrors % 100 == 0) + { + Logger.Warn("Can't send message to cat-server due to queue's full! Count: " + _mErrors); + } + } + } + } + + public void Shutdown() + { + _mActive = false; + + try + { + if (_mActiveChannel != null && _mActiveChannel.Connected) + { + _mActiveChannel.Close(); + } + } + catch + { + // ignore it + } + } + + #endregion + + public void ChannelManagementTask(object o) + { + while (true) + { + if (_mActive) + { + if (_mActiveChannel == null || !_mActiveChannel.Connected) + { + if (_mActiveChannel != null) + Logger.Warn("ChannelManagementTask中,Socket关闭"); + _mActiveIndex = _mClientConfig.Servers.Count; + } + + for (int i = 0; i < _mActiveIndex; i++) + { + TcpClient channel = CreateChannel(i); + + if (channel != null) + { + _mLastChannel = _mActiveChannel; + _mActiveChannel = channel; + _mActiveIndex = i; + break; + } + } + } + + Thread.Sleep(5 * 1000); // every 2 seconds + } + } + + public void AsynchronousSendTask(object o) + { + while (true) + { + if (_mActive) + { + while (_mQueue.Count == 0 || _mActiveChannel == null || !_mActiveChannel.Connected) + { + if (_mActiveChannel != null && !_mActiveChannel.Connected) + Logger.Warn("AsynchronousSendTask中,Socket关闭"); + Thread.Sleep(5 * 1000); + } + + IMessageTree tree = null; + + lock (_mQueue) + { + foreach (IMessageTree t in _mQueue) + { + tree = t; + break; + } + + _mQueue.RemoveAt(0); + } + + try + { + SendInternal(tree); + if (tree != null) tree.Message = null; + } + catch (Exception t) + { + Logger.Error("Error when sending message over TCP socket! Error: {0}", t); + } + } + else + { + Thread.Sleep(5 * 1000); + } + } + } + + private void SendInternal(IMessageTree tree) + { + if (_mLastChannel != null) + { + try + { + Logger.Warn("SendInternal中,_mLastChannel关闭"); + _mLastChannel.Close(); + } + catch + { + // ignore it + } + + _mLastChannel = null; + } + + if (_mActiveChannel != null && _mActiveChannel.Connected) + { + ChannelBuffer buf = new ChannelBuffer(8192); + + _mCodec.Encode(tree, buf); + + byte[] data = buf.ToArray(); + + _mActiveChannel.Client.Send(data); + + if (_mStatistics != null) + { + _mStatistics.OnBytes(data.Length); + } + } + else + { + Logger.Warn("SendInternal中,Socket关闭"); + } + } + + private TcpClient CreateChannel(int index) + { + Server server = _mClientConfig.Servers[index]; + + if (!server.Enabled) + { + return null; + } + + TcpClient socket = new TcpClient(); + + socket.NoDelay = true; + socket.ReceiveTimeout = 2 * 1000; // 2 seconds + + string ip = server.Ip; + int port = server.Port; + + Logger.Info("Connecting to server({0}:{1}) ...", ip, port); + + try + { + socket.Connect(ip, port); + + if (socket.Connected) + { + Logger.Info("Connected to server({0}:{1}).", ip, port); + + return socket; + } + Logger.Error("Failed to connect to server({0}:{1}).", ip, port); + } + catch (Exception e) + { + Logger.Error( + "Failed to connect to server({0}:{1}). Error: {2}.", + ip, + port, + e.Message + ); + } + + return null; + } + } } \ No newline at end of file diff --git a/Message/Spi/Internals/DefaultMessageManager.cs b/Message/Spi/Internals/DefaultMessageManager.cs index 19261b2..1cdad24 100644 --- a/Message/Spi/Internals/DefaultMessageManager.cs +++ b/Message/Spi/Internals/DefaultMessageManager.cs @@ -1,343 +1,344 @@ -using System.Globalization; -using Com.Dianping.Cat.Message.Internals; -using Com.Dianping.Cat.Message.Spi.IO; -using Com.Dianping.Cat.Util; -using Com.Dianping.Cat.Configuration; -using System; -using System.Collections.Generic; -using System.Threading; - -namespace Com.Dianping.Cat.Message.Spi.Internals -{ - public class DefaultMessageManager : IMessageManager - { - // we don't use static modifier since MessageManager is a singleton in - // production actually - private readonly CatThreadLocal _mContext = new CatThreadLocal(); - - private ClientConfig _mClientConfig; - - private MessageIdFactory _mFactory; - - private bool _mFirstMessage = true; - private String _mHostName; - - private IMessageSender _mSender; - - private IMessageStatistics _mStatistics; - - private StatusUpdateTask _mStatusUpdateTask; - - #region IMessageManager Members - - public virtual ClientConfig ClientConfig - { - get { return _mClientConfig; } - } - - public virtual ITransaction PeekTransaction - { - get - { - Context ctx = GetContext(); - - return ctx != null ? ctx.PeekTransaction() : null; - } - } - - public virtual IMessageTree ThreadLocalMessageTree - { - get - { - Context ctx = _mContext.Value; - - return ctx != null ? ctx.Tree : null; - } - } - - public virtual void Reset() - { - // destroy current thread local data - _mContext.Dispose(); - } - - public virtual void InitializeClient(ClientConfig clientConfig) - { - _mClientConfig = clientConfig ?? new ClientConfig(); - - _mHostName = NetworkInterfaceManager.GetLocalHostName(); - - _mStatistics = new DefaultMessageStatistics(); - _mSender = new TcpMessageSender(_mClientConfig, _mStatistics); - _mSender.Initialize(); - _mFactory = new MessageIdFactory(); - _mStatusUpdateTask = new StatusUpdateTask(_mStatistics); - - // initialize domain and ip address - _mFactory.Initialize(_mClientConfig.Domain.Id); - - // start status update task - ThreadPool.QueueUserWorkItem(_mStatusUpdateTask.Run); - - Logger.Info("Thread(StatusUpdateTask) started."); - } - - public virtual bool HasContext() - { - return _mContext.Value != null; - } - - public virtual bool CatEnabled - { - get { return _mClientConfig.Domain.Enabled && _mContext.Value != null; } - } - - public virtual void Add(IMessage message) - { - Context ctx = GetContext(); - - if (ctx != null) - { - ctx.Add(this, message); - } - else - Logger.Warn("Context没取到"); - } - - public virtual void Setup() - { - Context ctx = new Context(_mClientConfig.Domain.Id, _mHostName, - NetworkInterfaceManager.GetLocalHostAddress()); - - _mContext.Value = ctx; - } - - public virtual void Start(ITransaction transaction) - { - Context ctx = GetContext(); - - if (ctx != null) - { - ctx.Start(this, transaction); - } - else if (_mFirstMessage) - { - _mFirstMessage = false; - Logger.Info("CAT client is not enabled because it's not initialized yet"); - } - else - Logger.Warn("Context没取到"); - } - - public virtual void End(ITransaction transaction) - { - Context ctx = GetContext(); - - if (ctx != null) - { - //if (!transaction.Standalone) return; - if (ctx.End(this, transaction)) - { - _mContext.Dispose(); - } - } - else - Logger.Warn("Context没取到"); - } - - #endregion - - public MessageIdFactory GetMessageIdFactory() - { - return _mFactory; - } - - internal void Flush(IMessageTree tree) - { - if (_mSender != null) - { - _mSender.Send(tree); - - if (_mStatistics != null) - { - _mStatistics.OnSending(tree); - } - } - } - - internal Context GetContext() - { - if (Cat.IsInitialized()) - { - Context ctx = _mContext.Value; - - if (ctx != null) - { - return ctx; - } - if (_mClientConfig.DevMode) - { - throw new Exception( - "Cat has not been initialized successfully, please call Cal.setup(...) first for each thread."); - } - } - - return null; - } - - internal String NextMessageId() - { - return _mFactory.GetNextId(); - } - - //internal bool ShouldThrottle(IMessageTree tree) - //{ - // return false; - //} - - #region Nested type: Context - - internal class Context - { - private readonly Stack _mStack; - private readonly IMessageTree _mTree; - - public Context(String domain, String hostName, String ipAddress) - { - _mTree = new DefaultMessageTree(); - _mStack = new Stack(); - - Thread thread = Thread.CurrentThread; - String groupName = Thread.GetDomain().FriendlyName; - - _mTree.ThreadGroupName = groupName; - _mTree.ThreadId = thread.ManagedThreadId.ToString(CultureInfo.InvariantCulture); - _mTree.ThreadName = thread.Name; - - _mTree.Domain = domain; - _mTree.HostName = hostName; - _mTree.IpAddress = ipAddress; - } - - public IMessageTree Tree - { - get { return _mTree; } - } - - /// - /// 添加Event和Heartbeat - /// - /// - /// - public void Add(DefaultMessageManager manager, IMessage message) - { - if ((_mStack.Count == 0)) - { - IMessageTree tree = _mTree.Copy(); - tree.MessageId = manager.NextMessageId(); - tree.Message = message; - manager.Flush(tree); - } - else - { - ITransaction entry = _mStack.Peek(); - entry.AddChild(message); - } - } - - /// - /// return true means the transaction has been flushed. - /// - /// - /// - /// true if message is flushed, false otherwise - public bool End(DefaultMessageManager manager, ITransaction transaction) - { - if (_mStack.Count != 0) - { - ITransaction current = _mStack.Pop(); - while (transaction != current && _mStack.Count != 0) - { - current = _mStack.Pop(); - } - if (transaction != current) - throw new Exception("没找到对应的Transaction."); - - if (_mStack.Count == 0) - { - ValidateTransaction(current); - - IMessageTree tree = _mTree.Copy(); - _mTree.MessageId = null; - _mTree.Message = null; - manager.Flush(tree); - return true; - } - return false; - } - - throw new Exception("Stack为空, 没找到对应的Transaction."); - } - - /// - /// 返回stack的顶部对象 - /// - /// - public ITransaction PeekTransaction() - { - return (_mStack.Count == 0) ? null : _mStack.Peek(); - } - - /// - /// 添加transaction - /// - /// - /// - public void Start(DefaultMessageManager manager, ITransaction transaction) - { - if (_mStack.Count != 0) - { - transaction.Standalone = false; - ITransaction entry = _mStack.Peek(); - entry.AddChild(transaction); - } - else - { - _mTree.MessageId = manager.NextMessageId(); - _mTree.Message = transaction; - } - - _mStack.Push(transaction); - } - - //验证Transaction - internal void ValidateTransaction(ITransaction transaction) - { - IList children = transaction.Children; - int len = children.Count; - for (int i = 0; i < len; i++) - { - IMessage message = children[i]; - var transaction1 = message as ITransaction; - if (transaction1 != null) - { - ValidateTransaction(transaction1); - } - } - - if (!transaction.IsCompleted()) - { - // missing transaction end, log a BadInstrument event so that - // developer can fix the code - IMessage notCompleteEvent = new DefaultEvent("CAT", "BadInstrument") - {Status = "TransactionNotCompleted"}; - notCompleteEvent.Complete(); - transaction.AddChild(notCompleteEvent); - transaction.Complete(); - } - } - } - - #endregion - } +using System.Globalization; +using Com.Dianping.Cat.Message.Internals; +using Com.Dianping.Cat.Message.Spi.IO; +using Com.Dianping.Cat.Util; +using Com.Dianping.Cat.Configuration; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Com.Dianping.Cat.Message.Spi.Internals +{ + [Serializable] + public class DefaultMessageManager : IMessageManager + { + // we don't use static modifier since MessageManager is a singleton in + // production actually + private readonly CatThreadLocal _mContext = new CatThreadLocal(); + + private ClientConfig _mClientConfig; + + private MessageIdFactory _mFactory; + + private bool _mFirstMessage = true; + private String _mHostName; + + private IMessageSender _mSender; + + private IMessageStatistics _mStatistics; + + private StatusUpdateTask _mStatusUpdateTask; + + #region IMessageManager Members + + public virtual ClientConfig ClientConfig + { + get { return _mClientConfig; } + } + + public virtual ITransaction PeekTransaction + { + get + { + Context ctx = GetContext(); + + return ctx != null ? ctx.PeekTransaction() : null; + } + } + + public virtual IMessageTree ThreadLocalMessageTree + { + get + { + Context ctx = _mContext.Value; + + return ctx != null ? ctx.Tree : null; + } + } + + public virtual void Reset() + { + // destroy current thread local data + _mContext.Dispose(); + } + + public virtual void InitializeClient(ClientConfig clientConfig) + { + _mClientConfig = clientConfig ?? new ClientConfig(); + + _mHostName = NetworkInterfaceManager.GetLocalHostName(); + + _mStatistics = new DefaultMessageStatistics(); + _mSender = new TcpMessageSender(_mClientConfig, _mStatistics); + _mSender.Initialize(); + _mFactory = new MessageIdFactory(); + _mStatusUpdateTask = new StatusUpdateTask(_mStatistics); + + // initialize domain and ip address + _mFactory.Initialize(_mClientConfig.Domain.Id); + + // start status update task + // ThreadPool.QueueUserWorkItem(_mStatusUpdateTask.Run); + + Logger.Info("Thread(StatusUpdateTask) started."); + } + + public virtual bool HasContext() + { + return _mContext.Value != null; + } + + public virtual bool CatEnabled + { + get { return _mClientConfig.Domain.Enabled && _mContext.Value != null; } + } + + public virtual void Add(IMessage message) + { + Context ctx = GetContext(); + + if (ctx != null) + { + ctx.Add(this, message); + } + else + Logger.Warn("Context没取到"); + } + + public virtual void Setup() + { + Context ctx = new Context(_mClientConfig.Domain.Id, _mHostName, + NetworkInterfaceManager.GetLocalHostAddress()); + + _mContext.Value = ctx; + } + + public virtual void Start(ITransaction transaction) + { + Context ctx = GetContext(); + + if (ctx != null) + { + ctx.Start(this, transaction); + } + else if (_mFirstMessage) + { + _mFirstMessage = false; + Logger.Info("CAT client is not enabled because it's not initialized yet"); + } + else + Logger.Warn("Context没取到"); + } + + public virtual void End(ITransaction transaction) + { + Context ctx = GetContext(); + + if (ctx != null) + { + //if (!transaction.Standalone) return; + if (ctx.End(this, transaction)) + { + _mContext.Dispose(); + } + } + else + Logger.Warn("Context没取到"); + } + + #endregion + + public MessageIdFactory GetMessageIdFactory() + { + return _mFactory; + } + + internal void Flush(IMessageTree tree) + { + if (_mSender != null) + { + _mSender.Send(tree); + + if (_mStatistics != null) + { + _mStatistics.OnSending(tree); + } + } + } + + internal Context GetContext() + { + if (Cat.IsInitialized()) + { + Context ctx = _mContext.Value; + + if (ctx != null) + { + return ctx; + } + if (_mClientConfig.DevMode) + { + throw new Exception( + "Cat has not been initialized successfully, please call Cal.setup(...) first for each thread."); + } + } + + return null; + } + + public String NextMessageId() + { + return _mFactory.GetNextId(); + } + + //internal bool ShouldThrottle(IMessageTree tree) + //{ + // return false; + //} + + #region Nested type: Context + + internal class Context + { + private readonly Stack _mStack; + private readonly IMessageTree _mTree; + + public Context(String domain, String hostName, String ipAddress) + { + _mTree = new DefaultMessageTree(); + _mStack = new Stack(); + + Thread thread = Thread.CurrentThread; + String groupName = Thread.GetDomain().FriendlyName; + + _mTree.ThreadGroupName = groupName; + _mTree.ThreadId = thread.ManagedThreadId.ToString(CultureInfo.InvariantCulture); + _mTree.ThreadName = thread.Name; + + _mTree.Domain = domain; + _mTree.HostName = hostName; + _mTree.IpAddress = ipAddress; + } + + public IMessageTree Tree + { + get { return _mTree; } + } + + /// + /// 添加Event和Heartbeat + /// + /// + /// + public void Add(DefaultMessageManager manager, IMessage message) + { + if ((_mStack.Count == 0)) + { + IMessageTree tree = _mTree.Copy(); + tree.MessageId = manager.NextMessageId(); + tree.Message = message; + manager.Flush(tree); + } + else + { + ITransaction entry = _mStack.Peek(); + entry.AddChild(message); + } + } + + /// + /// return true means the transaction has been flushed. + /// + /// + /// + /// true if message is flushed, false otherwise + public bool End(DefaultMessageManager manager, ITransaction transaction) + { + if (_mStack.Count != 0) + { + ITransaction current = _mStack.Pop(); + while (transaction != current && _mStack.Count != 0) + { + current = _mStack.Pop(); + } + if (transaction != current) + throw new Exception("没找到对应的Transaction."); + + if (_mStack.Count == 0) + { + ValidateTransaction(current); + + IMessageTree tree = _mTree.Copy(); + _mTree.MessageId = null; + _mTree.Message = null; + manager.Flush(tree); + return true; + } + return false; + } + throw new Exception("Stack为空, 没找到对应的Transaction."); + + + } + + /// + /// 返回stack的顶部对象 + /// + /// + public ITransaction PeekTransaction() + { + return (_mStack.Count == 0) ? null : _mStack.Peek(); + } + + /// + /// 添加transaction + /// + /// + /// + public void Start(DefaultMessageManager manager, ITransaction transaction) + { + if (_mStack.Count != 0) + { + transaction.Standalone = false; + ITransaction entry = _mStack.Peek(); + entry.AddChild(transaction); + } + else + { + _mTree.MessageId = manager.NextMessageId(); + _mTree.Message = transaction; + } + + _mStack.Push(transaction); + } + + //验证Transaction + internal void ValidateTransaction(ITransaction transaction) + { + IList children = transaction.Children; + int len = children.Count; + for (int i = 0; i < len; i++) + { + IMessage message = children[i]; + var transaction1 = message as ITransaction; + if (transaction1 != null) + { + ValidateTransaction(transaction1); + } + } + + if (!transaction.IsCompleted()) + { + // missing transaction end, log a BadInstrument event so that + // developer can fix the code + IMessage notCompleteEvent = new DefaultEvent("CAT", "BadInstrument") { Status = "TransactionNotCompleted" }; + notCompleteEvent.Complete(); + transaction.AddChild(notCompleteEvent); + transaction.Complete(); + } + } + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Spi/Internals/DefaultMessageProducer.cs b/Message/Spi/Internals/DefaultMessageProducer.cs index a5722a5..e2e48cd 100644 --- a/Message/Spi/Internals/DefaultMessageProducer.cs +++ b/Message/Spi/Internals/DefaultMessageProducer.cs @@ -1,133 +1,134 @@ -using Com.Dianping.Cat.Message.Internals; -using System; -using System.IO; - -namespace Com.Dianping.Cat.Message.Spi.Internals -{ - public class DefaultMessageProducer : IMessageProducer - { - private readonly IMessageManager _mManager; - - public DefaultMessageProducer(IMessageManager manager) - { - _mManager = manager; +using Com.Dianping.Cat.Message.Internals; +using System; +using System.IO; + +namespace Com.Dianping.Cat.Message.Spi.Internals +{ + public class DefaultMessageProducer : IMessageProducer + { + private readonly IMessageManager _mManager; + + public DefaultMessageProducer(IMessageManager manager) + { + _mManager = manager; } + public String CreateMessageId() { return _mManager.GetMessageIdFactory().GetNextId(); - } - - #region IMessageProducer Members - - public virtual void LogError(Exception cause) - { - var ignore = false; - if (cause.Data.Contains("CatIgnore") && cause.Data["CatIgnore"] is bool) - ignore = (bool) (cause.Data["CatIgnore"]); - - if (ignore) return; - - StringWriter writer = new StringWriter(); - - try - { - writer.WriteLine(cause.Message); - writer.WriteLine(cause.StackTrace); - var innerException = cause.InnerException; - while (innerException != null) - { - writer.WriteLine("-------------------------------------------------------------------"); - writer.WriteLine(innerException.Message); - writer.WriteLine(innerException.StackTrace); - - innerException = innerException.InnerException; - } - } - catch - { - } - - LogEvent("Error", cause.GetType().FullName, "ERROR", - writer.ToString()); - } - - public virtual void LogEvent(String type, String name, String status, String nameValuePairs) - { - IEvent evt0 = NewEvent(type, name); - - if (!string.IsNullOrEmpty(nameValuePairs)) - { - evt0.AddData(nameValuePairs); - } - - evt0.Status = status; - evt0.Complete(); - } - - public virtual void LogHeartbeat(String type, String name, String status, String nameValuePairs) - { - IHeartbeat heartbeat = NewHeartbeat(type, name); - - if (!string.IsNullOrEmpty(nameValuePairs)) - { - heartbeat.AddData(nameValuePairs); - } - heartbeat.Status = status; - heartbeat.Complete(); - } - - public virtual IEvent NewEvent(String type, String name) - { - if (!_mManager.HasContext()) - { - _mManager.Setup(); - } - - if (_mManager.CatEnabled) - { - IEvent evt0 = new DefaultEvent(type, name); - - _mManager.Add(evt0); - return evt0; - } - return new NullEvent(); - } - - public virtual IHeartbeat NewHeartbeat(String type, String name) - { - if (!_mManager.HasContext()) - { - _mManager.Setup(); - } - - if (_mManager.CatEnabled) - { - IHeartbeat heartbeat = new DefaultHeartbeat(type, name); - - _mManager.Add(heartbeat); - return heartbeat; - } - return new NullHeartbeat(); - } - - public virtual ITransaction NewTransaction(String type, String name) - { - // this enable CAT client logging cat message without explicit setup - if (!_mManager.HasContext()) - { - _mManager.Setup(); - } - - if (_mManager.CatEnabled) - { - ITransaction transaction = new DefaultTransaction(type, name, _mManager.End); - - _mManager.Start(transaction); - return transaction; - } - return new NullTransaction(); - } - - #endregion - } + } + + #region IMessageProducer Members + + public virtual void LogError(Exception cause) + { + var ignore = false; + if (cause.Data.Contains("CatIgnore") && cause.Data["CatIgnore"] is bool) + ignore = (bool)(cause.Data["CatIgnore"]); + + if (ignore) return; + + StringWriter writer = new StringWriter(); + + try + { + writer.WriteLine(cause.Message); + writer.WriteLine(cause.StackTrace); + var innerException = cause.InnerException; + while (innerException != null) + { + writer.WriteLine("-------------------------------------------------------------------"); + writer.WriteLine(innerException.Message); + writer.WriteLine(innerException.StackTrace); + + innerException = innerException.InnerException; + } + } + catch + { + } + + LogEvent("Error", cause.GetType().FullName, "ERROR", + writer.ToString()); + } + + public virtual void LogEvent(String type, String name, String status, String nameValuePairs) + { + IEvent evt0 = NewEvent(type, name); + + if (!string.IsNullOrEmpty(nameValuePairs)) + { + evt0.AddData(nameValuePairs); + } + + evt0.Status = status; + evt0.Complete(); + } + + public virtual void LogHeartbeat(String type, String name, String status, String nameValuePairs) + { + IHeartbeat heartbeat = NewHeartbeat(type, name); + + if (!string.IsNullOrEmpty(nameValuePairs)) + { + heartbeat.AddData(nameValuePairs); + } + heartbeat.Status = status; + heartbeat.Complete(); + } + + public virtual IEvent NewEvent(String type, String name) + { + if (!_mManager.HasContext()) + { + _mManager.Setup(); + } + + if (_mManager.CatEnabled) + { + IEvent evt0 = new DefaultEvent(type, name); + + _mManager.Add(evt0); + return evt0; + } + return new NullEvent(); + } + + public virtual IHeartbeat NewHeartbeat(String type, String name) + { + if (!_mManager.HasContext()) + { + _mManager.Setup(); + } + + if (_mManager.CatEnabled) + { + IHeartbeat heartbeat = new DefaultHeartbeat(type, name); + + _mManager.Add(heartbeat); + return heartbeat; + } + return new NullHeartbeat(); + } + + public virtual ITransaction NewTransaction(String type, String name) + { + // this enable CAT client logging cat message without explicit setup + if (!_mManager.HasContext()) + { + _mManager.Setup(); + } + + if (_mManager.CatEnabled) + { + ITransaction transaction = new DefaultTransaction(type, name, _mManager.End); + + _mManager.Start(transaction); + return transaction; + } + return new NullTransaction(); + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Spi/Internals/DefaultMessageStatistics.cs b/Message/Spi/Internals/DefaultMessageStatistics.cs index 9ddc50c..a7990bc 100644 --- a/Message/Spi/Internals/DefaultMessageStatistics.cs +++ b/Message/Spi/Internals/DefaultMessageStatistics.cs @@ -1,30 +1,30 @@ -namespace Com.Dianping.Cat.Message.Spi.Internals -{ - public class DefaultMessageStatistics : IMessageStatistics - { - #region IMessageStatistics Members - - public long Produced { get; set; } - - public long Overflowed { get; set; } - - public long Bytes { get; set; } - - public void OnSending(IMessageTree tree) - { - Produced++; - } - - public void OnOverflowed(IMessageTree tree) - { - Overflowed++; - } - - public void OnBytes(int size) - { - Bytes += size; - } - - #endregion - } +namespace Com.Dianping.Cat.Message.Spi.Internals +{ + public class DefaultMessageStatistics : IMessageStatistics + { + #region IMessageStatistics Members + + public long Produced { get; set; } + + public long Overflowed { get; set; } + + public long Bytes { get; set; } + + public void OnSending(IMessageTree tree) + { + Produced++; + } + + public void OnOverflowed(IMessageTree tree) + { + Overflowed++; + } + + public void OnBytes(int size) + { + Bytes += size; + } + + #endregion + } } \ No newline at end of file diff --git a/Message/Spi/Internals/MessageIdFactory.cs b/Message/Spi/Internals/MessageIdFactory.cs index f45e874..d1d7411 100644 --- a/Message/Spi/Internals/MessageIdFactory.cs +++ b/Message/Spi/Internals/MessageIdFactory.cs @@ -1,86 +1,86 @@ -using Com.Dianping.Cat.Util; -using System; -using System.Text; - -namespace Com.Dianping.Cat.Message.Spi.Internals -{ - /// - /// 根据域名(配置指定的),系统IP(自动解析的,16进制字符串),时间戳(1970年到当前的小时数)和自增编号组成 - /// - public class MessageIdFactory - { - private String _mDomain; - private volatile int _mIndex; - - private String _mIpAddress; - private long _mLastTimestamp; - - public MessageIdFactory() - { - _mLastTimestamp = Timestamp; - } - - protected internal long Timestamp - { - get { return MilliSecondTimer.CurrentTimeHoursForJava(); } - } - - public String Domain - { - set { _mDomain = value; } - } - - public String IpAddress - { - set { _mIpAddress = value; } - } - - public String GetNextId() - { - long timestamp = Timestamp; - int index; - - lock (this) - { - if (timestamp != _mLastTimestamp) - { - _mIndex = 0; - _mLastTimestamp = timestamp; - } - - index = _mIndex++; - } - - StringBuilder sb = new StringBuilder(_mDomain.Length + 32); - - sb.Append(_mDomain); - sb.Append('-'); - sb.Append(_mIpAddress); - sb.Append('-'); - sb.Append(timestamp); - sb.Append('-'); - sb.Append(index); - - return sb.ToString(); - } - - public void Initialize(String domain) - { - _mDomain = domain; - - if (_mIpAddress != null) return; - - byte[] bytes = NetworkInterfaceManager.GetAddressBytes(); - - StringBuilder sb = new StringBuilder(); - - foreach (byte b in bytes) - { - sb.Append(((b >> 4) & 0x0F).ToString("x")); - sb.Append((b & 0x0F).ToString("x")); - } - - _mIpAddress = sb.ToString(); - } - } +using Com.Dianping.Cat.Util; +using System; +using System.Text; + +namespace Com.Dianping.Cat.Message.Spi.Internals +{ + /// + /// 根据域名(配置指定的),系统IP(自动解析的,16进制字符串),时间戳(1970年到当前的小时数)和自增编号组成 + /// + public class MessageIdFactory + { + private String _mDomain; + private volatile int _mIndex; + + private String _mIpAddress; + private long _mLastTimestamp; + + public MessageIdFactory() + { + _mLastTimestamp = Timestamp; + } + + protected internal long Timestamp + { + get { return MilliSecondTimer.CurrentTimeHoursForJava(); } + } + + public String Domain + { + set { _mDomain = value; } + } + + public String IpAddress + { + set { _mIpAddress = value; } + } + + public String GetNextId() + { + long timestamp = Timestamp; + int index; + + lock (this) + { + if (timestamp != _mLastTimestamp) + { + _mIndex = 0; + _mLastTimestamp = timestamp; + } + + index = _mIndex++; + } + + StringBuilder sb = new StringBuilder(_mDomain.Length + 32); + + sb.Append(_mDomain); + sb.Append('-'); + sb.Append(_mIpAddress); + sb.Append('-'); + sb.Append(timestamp); + sb.Append('-'); + sb.Append(index); + + return sb.ToString(); + } + + public void Initialize(String domain) + { + _mDomain = domain; + + if (_mIpAddress != null) return; + + byte[] bytes = NetworkInterfaceManager.GetAddressBytes(); + + StringBuilder sb = new StringBuilder(); + + foreach (byte b in bytes) + { + sb.Append(((b >> 4) & 0x0F).ToString("x")); + sb.Append((b & 0x0F).ToString("x")); + } + + _mIpAddress = sb.ToString(); + } + } } \ No newline at end of file diff --git a/Message/Spi/Internals/StatusUpdateTask.cs b/Message/Spi/Internals/StatusUpdateTask.cs index 649d44a..61f001a 100644 --- a/Message/Spi/Internals/StatusUpdateTask.cs +++ b/Message/Spi/Internals/StatusUpdateTask.cs @@ -1,192 +1,192 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Management; -using System.Threading; -using System.Text; -using Com.Dianping.Cat.Util; - -namespace Com.Dianping.Cat.Message.Spi.Internals -{ - public class StatusUpdateTask - { - private readonly IMessageStatistics _mStatistics; - - public StatusUpdateTask(IMessageStatistics mStatistics) - { - _mStatistics = mStatistics; - } - - /// - /// 获取系统内存大小 - /// - /// 内存大小(单位M) - private static int GetPhisicalMemory() - { - ManagementObjectSearcher searcher = new ManagementObjectSearcher(); //用于查询一些如系统信息的管理对象 - searcher.Query = new SelectQuery("Win32_PhysicalMemory ", "", new[] {"Capacity"}); //设置查询条件 - ManagementObjectCollection collection = searcher.Get(); //获取内存容量 - ManagementObjectCollection.ManagementObjectEnumerator em = collection.GetEnumerator(); - - long capacity = 0; - while (em.MoveNext()) - { - ManagementBaseObject baseObj = em.Current; - if (baseObj.Properties["Capacity"].Value != null) - { - try - { - capacity += long.Parse(baseObj.Properties["Capacity"].Value.ToString()); - } - catch - { - return 0; - } - } - } - return (int) (capacity/1024/1024); - } - - public void Run(object o) - { - while (true) - { - var sb = new StringBuilder(); - sb.AppendLine("StatusUpdateTask:"); - sb.AppendLine("Bytes=" + _mStatistics.Bytes); - sb.AppendLine("Overflowed=" + _mStatistics.Overflowed); - sb.AppendLine("Produced=" + _mStatistics.Produced); - - //机器的CPU内核数 - var processorCount = Environment.ProcessorCount; - sb.AppendLine("机器的CPU内核数=" + processorCount); - - //获取当前进程 - Process currentProcess = Process.GetCurrentProcess(); - //var processId = CurrentProcess.Id;//PID - //获取当前进程CPU利用率 - var tmpRate = - Math.Round( - currentProcess.UserProcessorTime.TotalMilliseconds*100/ - currentProcess.TotalProcessorTime.TotalMilliseconds, 2); - var cpuUsage = tmpRate + "%"; //CPU - //获取当前进程内存占用 - var memoryUsage = (currentProcess.WorkingSet64/1024/1024).ToString(CultureInfo.InvariantCulture) + "M (" + - (currentProcess.WorkingSet64/1024).ToString(CultureInfo.InvariantCulture) + "KB)"; - //占用内存 - //获取当前进程的线程数 - var threadCount = currentProcess.Threads.Count; //线程 - - //获取当前进程的线程池大小 - int maxThread, maxThread2; - ThreadPool.GetMaxThreads(out maxThread, out maxThread2); //线程 - int avThread, avThread2; - ThreadPool.GetAvailableThreads(out avThread, out avThread2); - int mThread, mThread2; - ThreadPool.GetMinThreads(out mThread, out mThread2); - - var threadPoolInfo = string.Format("线程池大小={0}/{1}, 线程池可用线程数={2}/{3}, 线程池最小线程数={4}/{5}", maxThread2, - maxThread, avThread2, avThread, mThread2, - mThread); - - sb.AppendLine("当前进程CPU利用率=" + cpuUsage); - sb.AppendLine("当前进程内存占用=" + memoryUsage); - sb.AppendLine("当前进程的线程数=" + threadCount); - sb.AppendLine(threadPoolInfo); - - var bestThreadInfo = string.Empty; - //最佳线程数为 - var bestThreadCountTmp = currentProcess.TotalProcessorTime.TotalMilliseconds/ - (currentProcess.UserProcessorTime.TotalMilliseconds/threadCount); - bestThreadInfo += "最佳线程数=" + bestThreadCountTmp + ","; - - //如果需要增加线程 - if (Math.Abs(bestThreadCountTmp - maxThread) > 1) - { - if (tmpRate < 90 && bestThreadCountTmp > 400) - { - int bestThreadCount = (int) Math.Floor(bestThreadCountTmp); - //ThreadPool.SetMinThreads(bestThreadCount, bestThreadCount); - ThreadPool.SetMaxThreads(bestThreadCount, bestThreadCount); - bestThreadInfo += "重设线程池的MaxThreads=" + bestThreadCount + ","; - } - } - sb.AppendLine(bestThreadInfo); - - var phisicalMemory = GetPhisicalMemory(); - sb.AppendLine("MemoryCapacity=" + phisicalMemory + "M"); - - PerformanceCounter ramCounter = new PerformanceCounter("Memory", "Available MBytes"); - double available = ramCounter.NextValue(); - - ////获取内存可用大小 - //ManagementClass cimobject2 = new ManagementClass("Win32_PerfFormattedData_PerfOS_Memory"); - //ManagementObjectCollection moc2 = cimobject2.GetInstances(); - //foreach (ManagementObject mo2 in moc2) - //{ - // available += int.Parse(mo2.Properties["AvailableMBytes"].Value.ToString(); - //} - //moc2.Dispose(); - //cimobject2.Dispose(); - - sb.AppendLine("MemoryAvailable=" + available + "M"); - sb.AppendLine("MemoryUsed=" + (phisicalMemory - available) + "M," + - Math.Round(((phisicalMemory - available)/phisicalMemory*100), 2) + "%"); - - PerformanceCounter cpuCounter = new PerformanceCounter - { - CategoryName = "Processor", - CounterName = "% Processor Time", - InstanceName = "_Total" - }; - sb.AppendLine("总CPU利用率=" + cpuCounter.NextValue() + "%"); - - //// Get the WMI class - //ManagementClass cc = new ManagementClass(new ManagementPath("Win32_Processor")); - //// Get the properties in the class - //ManagementObjectCollection moc = cc.GetInstances(); - //foreach (ManagementObject mo in moc) - //{ - // PropertyDataCollection properties = mo.Properties; //获取内核数代码 - // Console.WriteLine("物理内核数:" + properties["NumberOfCores"].Value); - // Console.WriteLine("逻辑内核数:" + properties["NumberOfLogicalProcessors"].Value); - // //其他属性获取代码 - // foreach (PropertyData property in properties) - // { - // Console.WriteLine(property.Name + ":" + property.Value); - // } - //} - //moc.Dispose(); - //cc.Dispose(); - - Logger.Info(sb.ToString()); - - var sb2 = new StringBuilder(); - sb2.Append("Bytes=" + _mStatistics.Bytes); - sb2.Append("&Overflowed=" + _mStatistics.Overflowed); - sb2.Append("&Produced=" + _mStatistics.Produced); - - sb2.AppendLine("&机器的CPU内核数=" + processorCount); - sb2.AppendLine("&当前进程CPU利用率=" + cpuUsage); - sb2.AppendLine("&当前进程内存占用=" + memoryUsage); - sb2.AppendLine("&当前进程的线程数=" + threadCount); - sb2.AppendLine("&" + threadPoolInfo); - sb2.AppendLine("&" + bestThreadInfo); - sb2.AppendLine("&MemoryCapacity=" + phisicalMemory + "M"); - sb2.AppendLine("&MemoryAvailable=" + available + "M"); - sb2.AppendLine("&MemoryUsed=" + (phisicalMemory - available) + "M," + - Math.Round(((phisicalMemory - available)/phisicalMemory*100), 2) + "%"); - sb2.AppendLine("&总CPU利用率=" + cpuCounter.NextValue() + "%"); - - Cat.GetProducer().LogHeartbeat("Cat", "Heartbeat", "0", sb2.ToString()); - Cat.GetProducer().LogEvent("Cat", "Heartbeat" + DateTime.Now.Minute, "0", sb2.ToString()); - - _mStatistics.Bytes = 0; - _mStatistics.Overflowed = 0; - _mStatistics.Produced = 0; - - Thread.Sleep(60000); - } - } - } +using System; +using System.Diagnostics; +using System.Globalization; +using System.Management; +using System.Threading; +using System.Text; +using Com.Dianping.Cat.Util; + +namespace Com.Dianping.Cat.Message.Spi.Internals +{ + public class StatusUpdateTask + { + private readonly IMessageStatistics _mStatistics; + + public StatusUpdateTask(IMessageStatistics mStatistics) + { + _mStatistics = mStatistics; + } + + /// + /// 获取系统内存大小 + /// + /// 内存大小(单位M) + private static int GetPhisicalMemory() + { + ManagementObjectSearcher searcher = new ManagementObjectSearcher(); //用于查询一些如系统信息的管理对象 + searcher.Query = new SelectQuery("Win32_PhysicalMemory ", "", new[] { "Capacity" }); //设置查询条件 + ManagementObjectCollection collection = searcher.Get(); //获取内存容量 + ManagementObjectCollection.ManagementObjectEnumerator em = collection.GetEnumerator(); + + long capacity = 0; + while (em.MoveNext()) + { + ManagementBaseObject baseObj = em.Current; + if (baseObj.Properties["Capacity"].Value != null) + { + try + { + capacity += long.Parse(baseObj.Properties["Capacity"].Value.ToString()); + } + catch + { + return 0; + } + } + } + return (int)(capacity / 1024 / 1024); + } + + public void Run(object o) + { + while (true) + { + var sb = new StringBuilder(); + sb.AppendLine("StatusUpdateTask:"); + sb.AppendLine("Bytes=" + _mStatistics.Bytes); + sb.AppendLine("Overflowed=" + _mStatistics.Overflowed); + sb.AppendLine("Produced=" + _mStatistics.Produced); + + //机器的CPU内核数 + var processorCount = Environment.ProcessorCount; + sb.AppendLine("机器的CPU内核数=" + processorCount); + + //获取当前进程 + Process currentProcess = Process.GetCurrentProcess(); + //var processId = CurrentProcess.Id;//PID + //获取当前进程CPU利用率 + var tmpRate = + Math.Round( + currentProcess.UserProcessorTime.TotalMilliseconds * 100 / + currentProcess.TotalProcessorTime.TotalMilliseconds, 2); + var cpuUsage = tmpRate + "%"; //CPU + //获取当前进程内存占用 + var memoryUsage = (currentProcess.WorkingSet64 / 1024 / 1024).ToString(CultureInfo.InvariantCulture) + "M (" + + (currentProcess.WorkingSet64 / 1024).ToString(CultureInfo.InvariantCulture) + "KB)"; + //占用内存 + //获取当前进程的线程数 + var threadCount = currentProcess.Threads.Count; //线程 + + //获取当前进程的线程池大小 + int maxThread, maxThread2; + ThreadPool.GetMaxThreads(out maxThread, out maxThread2); //线程 + int avThread, avThread2; + ThreadPool.GetAvailableThreads(out avThread, out avThread2); + int mThread, mThread2; + ThreadPool.GetMinThreads(out mThread, out mThread2); + + var threadPoolInfo = string.Format("线程池大小={0}/{1}, 线程池可用线程数={2}/{3}, 线程池最小线程数={4}/{5}", maxThread2, + maxThread, avThread2, avThread, mThread2, + mThread); + + sb.AppendLine("当前进程CPU利用率=" + cpuUsage); + sb.AppendLine("当前进程内存占用=" + memoryUsage); + sb.AppendLine("当前进程的线程数=" + threadCount); + sb.AppendLine(threadPoolInfo); + + var bestThreadInfo = string.Empty; + //最佳线程数为 + var bestThreadCountTmp = currentProcess.TotalProcessorTime.TotalMilliseconds / + (currentProcess.UserProcessorTime.TotalMilliseconds / threadCount); + bestThreadInfo += "最佳线程数=" + bestThreadCountTmp + ","; + + //如果需要增加线程 + if (Math.Abs(bestThreadCountTmp - maxThread) > 1) + { + if (tmpRate < 90 && bestThreadCountTmp > 400) + { + int bestThreadCount = (int)Math.Floor(bestThreadCountTmp); + //ThreadPool.SetMinThreads(bestThreadCount, bestThreadCount); + ThreadPool.SetMaxThreads(bestThreadCount, bestThreadCount); + bestThreadInfo += "重设线程池的MaxThreads=" + bestThreadCount + ","; + } + } + sb.AppendLine(bestThreadInfo); + + var phisicalMemory = GetPhisicalMemory(); + sb.AppendLine("MemoryCapacity=" + phisicalMemory + "M"); + + PerformanceCounter ramCounter = new PerformanceCounter("Memory", "Available MBytes"); + double available = ramCounter.NextValue(); + + ////获取内存可用大小 + //ManagementClass cimobject2 = new ManagementClass("Win32_PerfFormattedData_PerfOS_Memory"); + //ManagementObjectCollection moc2 = cimobject2.GetInstances(); + //foreach (ManagementObject mo2 in moc2) + //{ + // available += int.Parse(mo2.Properties["AvailableMBytes"].Value.ToString(); + //} + //moc2.Dispose(); + //cimobject2.Dispose(); + + sb.AppendLine("MemoryAvailable=" + available + "M"); + sb.AppendLine("MemoryUsed=" + (phisicalMemory - available) + "M," + + Math.Round(((phisicalMemory - available) / phisicalMemory * 100), 2) + "%"); + + PerformanceCounter cpuCounter = new PerformanceCounter + { + CategoryName = "Processor", + CounterName = "% Processor Time", + InstanceName = "_Total" + }; + sb.AppendLine("总CPU利用率=" + cpuCounter.NextValue() + "%"); + + //// Get the WMI class + //ManagementClass cc = new ManagementClass(new ManagementPath("Win32_Processor")); + //// Get the properties in the class + //ManagementObjectCollection moc = cc.GetInstances(); + //foreach (ManagementObject mo in moc) + //{ + // PropertyDataCollection properties = mo.Properties; //获取内核数代码 + // Console.WriteLine("物理内核数:" + properties["NumberOfCores"].Value); + // Console.WriteLine("逻辑内核数:" + properties["NumberOfLogicalProcessors"].Value); + // //其他属性获取代码 + // foreach (PropertyData property in properties) + // { + // Console.WriteLine(property.Name + ":" + property.Value); + // } + //} + //moc.Dispose(); + //cc.Dispose(); + + Logger.Info(sb.ToString()); + + var sb2 = new StringBuilder(); + sb2.Append("Bytes=" + _mStatistics.Bytes); + sb2.Append("&Overflowed=" + _mStatistics.Overflowed); + sb2.Append("&Produced=" + _mStatistics.Produced); + + sb2.AppendLine("&机器的CPU内核数=" + processorCount); + sb2.AppendLine("&当前进程CPU利用率=" + cpuUsage); + sb2.AppendLine("&当前进程内存占用=" + memoryUsage); + sb2.AppendLine("&当前进程的线程数=" + threadCount); + sb2.AppendLine("&" + threadPoolInfo); + sb2.AppendLine("&" + bestThreadInfo); + sb2.AppendLine("&MemoryCapacity=" + phisicalMemory + "M"); + sb2.AppendLine("&MemoryAvailable=" + available + "M"); + sb2.AppendLine("&MemoryUsed=" + (phisicalMemory - available) + "M," + + Math.Round(((phisicalMemory - available) / phisicalMemory * 100), 2) + "%"); + sb2.AppendLine("&总CPU利用率=" + cpuCounter.NextValue() + "%"); + + Cat.GetProducer().LogHeartbeat("Cat", "Heartbeat", "0", sb2.ToString()); + Cat.GetProducer().LogEvent("Cat", "Heartbeat" + DateTime.Now.Minute, "0", sb2.ToString()); + + _mStatistics.Bytes = 0; + _mStatistics.Overflowed = 0; + _mStatistics.Produced = 0; + + Thread.Sleep(60000); + } + } + } } \ No newline at end of file diff --git a/Util/AppEnv.cs b/Util/AppEnv.cs index a3f277c..40a6fae 100644 --- a/Util/AppEnv.cs +++ b/Util/AppEnv.cs @@ -9,13 +9,7 @@ namespace Com.Dianping.Cat.Util { public static class AppEnv { - - static AppEnv() - { - IP = getLocalIP(); - } - - public static string IP { get; private set; } + public static string IP { get { return getLocalIP(); } } private static string getLocalIP() { @@ -46,5 +40,14 @@ public static string GetClientIp(HttpRequest request) } return request.ServerVariables["REMOTE_ADDR"]; } + + public static string GetRemoteIp(HttpRequest request) + { + if (request == null) + { + return string.Empty; + } + return request.ServerVariables["REMOTE_ADDR"]; + } } } diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 4bf1231..3686002 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -2,6 +2,7 @@ using Com.Dianping.Cat.Util; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net; using System.Text; @@ -9,21 +10,21 @@ namespace Com.Dianping.Cat.Util { - public class CatHelper + public static class CatHelper { public const string CatRootId = "X-Cat-RootId"; public const string CatParentId = "X-Cat-ParentId"; public const string CatId = "X-Cat-Id"; - public static ITransaction BeginClientTransaction(string name, string requestingUrl, WebRequest request) + public static ITransaction BeginClientTransaction(string type, string name, WebRequest request) { if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) return null; try { - var tran = Cat.GetProducer().NewTransaction("HttpCall", name); + var tran = Cat.GetProducer().NewTransaction(type, name); tran.Status = "0"; - Cat.GetProducer().LogEvent("HttpCall.Server", "HttpRequest", "0", requestingUrl); + Cat.GetProducer().LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { @@ -35,12 +36,9 @@ public static ITransaction BeginClientTransaction(string name, string requesting string currentMessageId = tree.MessageId; Cat.GetProducer().LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); - if (request != null) - { - request.Headers.Add(CatHelper.CatRootId, rootMessageId); - request.Headers.Add(CatHelper.CatParentId, currentMessageId); - request.Headers.Add(CatHelper.CatId, serverMessageId); - } + request.Headers.Add(CatHelper.CatRootId, rootMessageId); + request.Headers.Add(CatHelper.CatParentId, currentMessageId); + request.Headers.Add(CatHelper.CatId, serverMessageId); return tran; } @@ -50,8 +48,9 @@ public static ITransaction BeginClientTransaction(string name, string requesting } } - public static ITransaction BeginServerTransaction(string url, string name) + public static ITransaction BeginServerTransaction(string type, string name = null, HttpResponse response = null) { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) return null; try @@ -59,17 +58,22 @@ public static ITransaction BeginServerTransaction(string url, string name) var httpContext = System.Web.HttpContext.Current; if (httpContext == null) { return null; } var request = httpContext.Request; + string rootMessageId = request.Headers[CatHelper.CatRootId]; string serverMessageId = request.Headers[CatHelper.CatParentId]; string currentMessageId = request.Headers[CatHelper.CatId]; - var tran = Cat.GetProducer().NewTransaction("HttpService", url); + if (string.IsNullOrWhiteSpace(name)) + name = request.Path; + + var tran = Cat.GetProducer().NewTransaction(type, name); var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { Cat.GetManager().Setup(); tree = Cat.GetManager().ThreadLocalMessageTree; } + tree.RootMessageId = rootMessageId; tree.ParentMessageId = serverMessageId; if (!string.IsNullOrEmpty(currentMessageId)) @@ -77,7 +81,19 @@ public static ITransaction BeginServerTransaction(string url, string name) tree.MessageId = currentMessageId; } tran.Status = "0"; - Cat.GetProducer().LogEvent("HttpService.Server", name, "0", AppEnv.GetClientIp(request)); + + if (response != null) + { + if (!string.IsNullOrWhiteSpace(rootMessageId)) + response.AddHeader(CatHelper.CatRootId, rootMessageId); + if (!string.IsNullOrWhiteSpace(currentMessageId)) + response.AddHeader(CatHelper.CatParentId, currentMessageId); + response.AddHeader(CatHelper.CatId, tree.MessageId); + } + + Cat.GetProducer().LogEvent(type, type + ".Server", "0", getURLServerValue(request)); + Cat.GetProducer().LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); + Cat.GetProducer().LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); return tran; } catch @@ -102,7 +118,7 @@ public static ITransaction BeginTransaction(string type, string name) } } - public static void AddServerEvent(string type, string name) + public static void AddLogEvent(string type, string name) { if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) return; @@ -111,8 +127,8 @@ public static void AddServerEvent(string type, string name) public static string GetRootMessageId() { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return string.Empty; + if (!Cat.IsInitialized()) { return string.Empty; } + if (!Cat.GetManager().CatEnabled) { return string.Empty; } var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { @@ -130,8 +146,8 @@ public static string GetRootMessageId() public static string GetMessageId() { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return string.Empty; + if (!Cat.IsInitialized()) { return string.Empty; } + if (!Cat.GetManager().CatEnabled) { return string.Empty; } var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { @@ -164,5 +180,42 @@ public static void CompleteTrancation(ITransaction tran) tran.Complete(); } } + + private static string getURLServerValue(HttpRequest request) + { + if (request == null) + return string.Empty; + StringBuilder sb = new StringBuilder(); + sb.Append("IPS=").Append(AppEnv.GetClientIp(request)); + sb.Append("&VirtualIP=").Append(AppEnv.GetRemoteIp(request)); + sb.Append("&Server=").Append(AppEnv.IP); + sb.Append("&Referer=").Append(request.getHeader("Referer")); + sb.Append("&Agent=").Append(request.getHeader("User-Agent")); + return sb.ToString(); + } + + private static string getURLMethodValue(HttpRequest request) + { + StringBuilder sb = new StringBuilder(); + sb.Append(request.getServerVariables("Request_Method")).Append(" "); + sb.Append(request.Url.ToString()); + return sb.ToString(); + } + + private static string getHeader(this HttpRequest request, string key) + { + if (request == null) + request = HttpContext.Current.Request; + var headerKey = request.Headers.AllKeys.Where(p => p.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + return request.Headers[headerKey] ?? "null"; + } + + private static string getServerVariables(this HttpRequest request, string key) + { + if (request == null) + request = HttpContext.Current.Request; + var headerKey = request.ServerVariables.AllKeys.Where(p => p.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + return request.ServerVariables[headerKey] ?? "null"; + } } } diff --git a/Util/CatThreadLocal.cs b/Util/CatThreadLocal.cs index 3d92ce8..68f9922 100644 --- a/Util/CatThreadLocal.cs +++ b/Util/CatThreadLocal.cs @@ -1,34 +1,34 @@ -using System.Collections; -using System.Threading; - -namespace Com.Dianping.Cat.Util -{ - public class CatThreadLocal - { - private readonly Hashtable _mValues = new Hashtable(1024); - - public T Value - { - get - { - int threadId = Thread.CurrentThread.ManagedThreadId; - object value = _mValues[threadId]; - - return (T) value; - } - set - { - int threadId = Thread.CurrentThread.ManagedThreadId; - - _mValues[threadId] = value; - } - } - - public void Dispose() - { - int threadId = Thread.CurrentThread.ManagedThreadId; - - _mValues.Remove(threadId); - } - } +using System.Collections; +using System.Threading; + +namespace Com.Dianping.Cat.Util +{ + public class CatThreadLocal + { + private readonly Hashtable _mValues = new Hashtable(1024); + + public T Value + { + get + { + int threadId = Thread.CurrentThread.ManagedThreadId; + object value = _mValues[threadId]; + + return (T)value; + } + set + { + int threadId = Thread.CurrentThread.ManagedThreadId; + + _mValues[threadId] = value; + } + } + + public void Dispose() + { + int threadId = Thread.CurrentThread.ManagedThreadId; + + _mValues.Remove(threadId); + } + } } \ No newline at end of file diff --git a/Util/Logger.cs b/Util/Logger.cs index 5cb4494..e40b5cd 100644 --- a/Util/Logger.cs +++ b/Util/Logger.cs @@ -1,67 +1,67 @@ -using System; -using System.IO; - -namespace Com.Dianping.Cat.Util -{ - /// - /// 简单记录Cat客户端的启动日志 - /// - public class Logger - { - private static StreamWriter _mWriter; - - public static void Info(string pattern, params object[] args) - { - Log("INFO", pattern, args); - } - - public static void Warn(string pattern, params object[] args) - { - Log("WARN", pattern, args); - } - - public static void Error(string pattern, params object[] args) - { - Log("ERROR", pattern, args); - } - - private static void Log(string severity, string pattern, params object[] args) - { - string timestamp = new DateTime(MilliSecondTimer.CurrentTimeMicros()*10L).ToString("yyyy-MM-dd HH:mm:ss.fff"); - string message = string.Format(pattern, args); - string line = "[" + timestamp + "] [" + severity + "] " + message; - - if (_mWriter != null) - { - _mWriter.WriteLine(line); - _mWriter.Flush(); - } - else - { - Console.WriteLine(line); - } - } - - /// - /// 初始化 - /// - /// - public static void Initialize(string logFile) - { - try - { - if (!File.Exists(logFile)) - { - var directoryInfo = new FileInfo(logFile).Directory; - if (directoryInfo != null) directoryInfo.Create(); - } - - _mWriter = new StreamWriter(logFile, true); - } - catch (Exception e) - { - Console.WriteLine("Error when openning log file: " + e.Message + " " + e.StackTrace + "."); - } - } - } +using System; +using System.IO; + +namespace Com.Dianping.Cat.Util +{ + /// + /// 简单记录Cat客户端的启动日志 + /// + public class Logger + { + private static StreamWriter _mWriter; + + public static void Info(string pattern, params object[] args) + { + Log("INFO", pattern, args); + } + + public static void Warn(string pattern, params object[] args) + { + Log("WARN", pattern, args); + } + + public static void Error(string pattern, params object[] args) + { + Log("ERROR", pattern, args); + } + + private static void Log(string severity, string pattern, params object[] args) + { + string timestamp = new DateTime(MilliSecondTimer.CurrentTimeMicros() * 10L).ToString("yyyy-MM-dd HH:mm:ss.fff"); + string message = string.Format(pattern, args); + string line = "[" + timestamp + "] [" + severity + "] " + message; + + if (_mWriter != null) + { + _mWriter.WriteLine(line); + _mWriter.Flush(); + } + else + { + Console.WriteLine(line); + } + } + + /// + /// 初始化 + /// + /// + public static void Initialize(string logFile) + { + try + { + if (!File.Exists(logFile)) + { + var directoryInfo = new FileInfo(logFile).Directory; + if (directoryInfo != null) directoryInfo.Create(); + } + + _mWriter = new StreamWriter(logFile, true); + } + catch (Exception e) + { + Console.WriteLine("Error when openning log file: " + e.Message + " " + e.StackTrace + "."); + } + } + } } \ No newline at end of file diff --git a/Util/MilliSecondTimer.cs b/Util/MilliSecondTimer.cs index c19d959..95df7dc 100644 --- a/Util/MilliSecondTimer.cs +++ b/Util/MilliSecondTimer.cs @@ -1,68 +1,68 @@ -using System; -using System.Runtime.InteropServices; - -namespace Com.Dianping.Cat.Util -{ - /// - /// This timer provides milli-second precise system time. - /// - public class MilliSecondTimer - { - public static long CurrentTimeMicros() - { - //return HighResTicksProvider.GetTickCount () / 10L; // it's microsecond precise - return DateTime.Now.Ticks/10L; // it's millisecond precise - } - - public static long CurrentTimeHoursForJava() - { - DateTime baseline = new DateTime(1970, 1, 1, 0, 0, 0); - TimeSpan ts = new TimeSpan(DateTime.UtcNow.Ticks - baseline.Ticks); - - return ((long) ts.TotalMilliseconds/3600000L); - } - } - - public class HighResTicksProvider - { - private static long _f; - - [DllImport("kernel32.dll")] - private static extern bool QueryPerformanceCounter([In, Out] ref long lpPerformanceCount); - - [DllImport("kernel32.dll")] - private static extern bool QueryPerformanceFrequency([In, Out] ref long lpFrequency); - - /// - /// 获得当前时间戳,十分之一微秒(100纳秒,和 DateTime.Now.Ticks 刻度一样) - /// - /// - public static long GetTickCount() - { - long f = _f; - - if (f == 0) - { - if (QueryPerformanceFrequency(ref f)) - { - _f = f; - } - else - { - _f = -1; - } - } - - if (_f == -1) - { - // fallback - return DateTime.Now.Ticks; - } - - long c = 0; - QueryPerformanceCounter(ref c); - - return (long) (((double) c)*1000*10000/(f)); - } - } +using System; +using System.Runtime.InteropServices; + +namespace Com.Dianping.Cat.Util +{ + /// + /// This timer provides milli-second precise system time. + /// + public class MilliSecondTimer + { + public static long CurrentTimeMicros() + { + //return HighResTicksProvider.GetTickCount () / 10L; // it's microsecond precise + return DateTime.Now.Ticks / 10L; // it's millisecond precise + } + + public static long CurrentTimeHoursForJava() + { + DateTime baseline = new DateTime(1970, 1, 1, 0, 0, 0); + TimeSpan ts = new TimeSpan(DateTime.UtcNow.Ticks - baseline.Ticks); + + return ((long)ts.TotalMilliseconds / 3600000L); + } + } + + public class HighResTicksProvider + { + private static long _f; + + [DllImport("kernel32.dll")] + private static extern bool QueryPerformanceCounter([In, Out] ref long lpPerformanceCount); + + [DllImport("kernel32.dll")] + private static extern bool QueryPerformanceFrequency([In, Out] ref long lpFrequency); + + /// + /// 获得当前时间戳,十分之一微秒(100纳秒,和 DateTime.Now.Ticks 刻度一样) + /// + /// + public static long GetTickCount() + { + long f = _f; + + if (f == 0) + { + if (QueryPerformanceFrequency(ref f)) + { + _f = f; + } + else + { + _f = -1; + } + } + + if (_f == -1) + { + // fallback + return DateTime.Now.Ticks; + } + + long c = 0; + QueryPerformanceCounter(ref c); + + return (long)(((double)c) * 1000 * 10000 / (f)); + } + } } \ No newline at end of file diff --git a/Util/NetworkInterfaceManager.cs b/Util/NetworkInterfaceManager.cs index 19b8e4c..0349f38 100644 --- a/Util/NetworkInterfaceManager.cs +++ b/Util/NetworkInterfaceManager.cs @@ -1,39 +1,39 @@ -using System; -using System.Linq; -using System.Net.Sockets; -using System.Net; - -namespace Com.Dianping.Cat.Util -{ - public class NetworkInterfaceManager - { - public static string GetLocalHostName() - { - return Dns.GetHostName(); - } - - public static string GetLocalHostAddress() - { - IPHostEntry host = Dns.GetHostEntry(GetLocalHostName()); - - foreach (IPAddress ip in host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork)) - { - return ip.ToString(); - } - - throw new NotSupportedException("No IP address found"); - } - - public static byte[] GetAddressBytes() - { - IPHostEntry host = Dns.GetHostEntry(GetLocalHostName()); - - foreach (IPAddress ip in host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork)) - { - return ip.GetAddressBytes(); - } - - throw new NotSupportedException("No IP address found"); - } - } +using System; +using System.Linq; +using System.Net.Sockets; +using System.Net; + +namespace Com.Dianping.Cat.Util +{ + public class NetworkInterfaceManager + { + public static string GetLocalHostName() + { + return Dns.GetHostName(); + } + + public static string GetLocalHostAddress() + { + IPHostEntry host = Dns.GetHostEntry(GetLocalHostName()); + + foreach (IPAddress ip in host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork)) + { + return ip.ToString(); + } + + throw new NotSupportedException("No IP address found"); + } + + public static byte[] GetAddressBytes() + { + IPHostEntry host = Dns.GetHostEntry(GetLocalHostName()); + + foreach (IPAddress ip in host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork)) + { + return ip.GetAddressBytes(); + } + + throw new NotSupportedException("No IP address found"); + } + } } \ No newline at end of file diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs new file mode 100644 index 0000000..ed0fc6c --- /dev/null +++ b/Web/CatHttpAsyncHandler.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using System.Web.SessionState; +using Com.Dianping.Cat.Util; +using System.Threading; + +namespace Com.Dianping.Cat.Web +{ + public class CatHttpAsyncHandler : IHttpAsyncHandler, IRequiresSessionState + { + public CatHttpAsyncHandler(IHttpAsyncHandler asyncHandler) + { + this.handler = asyncHandler; + } + + public IHttpAsyncHandler handler; + + public bool IsReusable { get { return handler.IsReusable; } } + + public void ProcessRequest(HttpContext context) + { + var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + try + { + handler.ProcessRequest(context); + } + catch (Exception ex) + { + var baseEx = ex.GetBaseException(); + CatHelper.SetTrancationStatus(tran, baseEx); + CatHelper.AddLogEvent("Exception", baseEx.StackTrace); + CatHelper.CompleteTrancation(tran); + throw ex; + } + CatHelper.CompleteTrancation(tran); + } + + public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) + { + return handler.BeginProcessRequest(context, cb, extraData); + } + + public void EndProcessRequest(IAsyncResult result) + { + handler.EndProcessRequest(result); + } + } +} diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index 84781ef..eee557a 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -5,24 +5,33 @@ using System.Web; using System.Web.SessionState; using Com.Dianping.Cat.Util; +using System.Threading; namespace Com.Dianping.Cat.Web { public class CatHttpHandler : IHttpHandler, IRequiresSessionState { - public IHttpHandler Handler { get; set; } - public bool IsReusable { get { return Handler.IsReusable; } } + public CatHttpHandler(IHttpHandler httpHandler) + { + this.handler = httpHandler; + } + + private IHttpHandler handler; + + public bool IsReusable { get { return handler.IsReusable; } } public void ProcessRequest(HttpContext context) { - var tran = CatHelper.BeginServerTransaction(context.Request.Url.ToString(), Handler.GetType().FullName); + var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { - Handler.ProcessRequest(context); + handler.ProcessRequest(context); } catch (Exception ex) { - CatHelper.SetTrancationStatus(tran, ex); + var baseEx = ex.GetBaseException(); + CatHelper.SetTrancationStatus(tran, baseEx); + CatHelper.AddLogEvent("Exception", baseEx.StackTrace); CatHelper.CompleteTrancation(tran); throw ex; } diff --git a/Web/CatHttpModule.cs b/Web/CatHttpModule.cs index 7ebd7ac..ced1892 100644 --- a/Web/CatHttpModule.cs +++ b/Web/CatHttpModule.cs @@ -11,58 +11,27 @@ namespace Com.Dianping.Cat.Web { public class CatHttpModule : IHttpModule { - public void Init(HttpApplication context) { context.PostMapRequestHandler += context_PostMapRequestHandler; } - - void context_PostMapRequestHandler(object sender, EventArgs e) { if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) { return; } - try - { - var context = System.Web.HttpContext.Current; - var request = System.Web.HttpContext.Current.Request; - - if (filter_Handler(context))//如果被过滤了,就直接跳过 - return; - - var handler = new CatHttpHandler { Handler = context.Handler }; - context.Handler = handler; - } - catch (Exception) - { - - } - } - + var context = System.Web.HttpContext.Current; - bool filter_Handler(HttpContext context) - { - var request = context.Request; - var handlerType = context.Handler.GetType(); - var attibutes = handlerType.GetCustomAttributes(typeof(Attribute), false); - - if (context == null) - return true; - - if (context.Handler.GetType().FullName.Equals("System.Web.Handlers.TransferRequestHandler", StringComparison.OrdinalIgnoreCase)) - return true; - - if (context.Handler == null && File.Exists(request.PhysicalPath)) - return true; - - if (attibutes.Any(p => p.GetType().FullName == "TongCheng.SOA.Interface.Attributes.SOABusiness")) - return true; + if (context == null || context.Handler == null) + return; - return false; + if (context.Handler is IHttpAsyncHandler) + context.Handler = new CatHttpAsyncHandler((IHttpAsyncHandler)context.Handler); + else + context.Handler = new CatHttpHandler(context.Handler); } public void Dispose() From d6c1a40d1ca7fac7c922908ed32ef9a69c5b3a0e Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 16 Jan 2015 12:55:05 +0800 Subject: [PATCH 05/21] =?UTF-8?q?=E5=8A=A0=E5=85=A5Metric=E6=94=AF?= =?UTF-8?q?=E6=8C=81=EF=BC=8Chttphandler=E5=BF=BD=E7=95=A5=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E6=AD=A2=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Message/IMetric.cs | 6 + Message/Internals/AbstractMessage.cs | 4 +- Message/Internals/DefaultMetric.cs | 12 ++ Message/Internals/NullMetric.cs | 11 ++ Message/Spi/Codec/PlainTextMessageCodec.cs | 185 ++++++++++-------- Message/Spi/IMessageProducer.cs | 18 +- .../Spi/Internals/DefaultMessageProducer.cs | 38 ++++ Util/CatHelper.cs | 52 ++++- Web/CatHttpAsyncHandler.cs | 11 +- Web/CatHttpHandler.cs | 11 +- 10 files changed, 249 insertions(+), 99 deletions(-) create mode 100644 Message/IMetric.cs create mode 100644 Message/Internals/DefaultMetric.cs create mode 100644 Message/Internals/NullMetric.cs diff --git a/Message/IMetric.cs b/Message/IMetric.cs new file mode 100644 index 0000000..3ef0947 --- /dev/null +++ b/Message/IMetric.cs @@ -0,0 +1,6 @@ +namespace Com.Dianping.Cat.Message +{ + public interface IMetric : IMessage + { + } +} diff --git a/Message/Internals/AbstractMessage.cs b/Message/Internals/AbstractMessage.cs index 1096f7a..2b0ad48 100644 --- a/Message/Internals/AbstractMessage.cs +++ b/Message/Internals/AbstractMessage.cs @@ -31,7 +31,7 @@ protected AbstractMessage(String type, String name) public String Data { - get { return _mData == null ? "" : _mData.ToString(); } + get { return _mData == null || _mData.Length == 0 ? string.Empty : _mData.ToString(); } } public String Name @@ -68,7 +68,7 @@ public void AddData(String keyValuePairs) } else { - _mData.Append(keyValuePairs); + _mData.Append('&').Append(keyValuePairs); } } diff --git a/Message/Internals/DefaultMetric.cs b/Message/Internals/DefaultMetric.cs new file mode 100644 index 0000000..74ed63a --- /dev/null +++ b/Message/Internals/DefaultMetric.cs @@ -0,0 +1,12 @@ +using System; + +namespace Com.Dianping.Cat.Message.Internals +{ + public class DefaultMetric : AbstractMessage, IMetric + { + public DefaultMetric(String type, String name) + : base(type, name) + { + } + } +} diff --git a/Message/Internals/NullMetric.cs b/Message/Internals/NullMetric.cs new file mode 100644 index 0000000..4d5bf52 --- /dev/null +++ b/Message/Internals/NullMetric.cs @@ -0,0 +1,11 @@ + +namespace Com.Dianping.Cat.Message.Internals +{ + public class NullMetric : AbstractMessage, IMetric + { + public NullMetric() + : base(null, null) + { + } + } +} diff --git a/Message/Spi/Codec/PlainTextMessageCodec.cs b/Message/Spi/Codec/PlainTextMessageCodec.cs index a8be51f..bfc40d7 100644 --- a/Message/Spi/Codec/PlainTextMessageCodec.cs +++ b/Message/Spi/Codec/PlainTextMessageCodec.cs @@ -113,103 +113,112 @@ protected internal IMessage DecodeLine(ChannelBuffer buf, ITransaction parent, Stack stack, IMessageTree tree) { BufferHelper helper = _mBufferHelper; - byte identifier = buf.ReadByte(); + char identifier = (char)buf.ReadByte(); String timestamp = helper.Read(buf, TAB); String type = helper.Read(buf, TAB); String name = helper.Read(buf, TAB); - - if (identifier == 'E') + switch (identifier) { - IMessage evt = new DefaultEvent(type, name); - String status = helper.Read(buf, TAB); - String data = helper.ReadRaw(buf, TAB); + case 't': + IMessage transaction = new DefaultTransaction(type, name, null); - helper.Read(buf, LF); // get rid of line feed - evt.Timestamp = _mDateHelper.Parse(timestamp); - evt.Status = status; - evt.AddData(data); + helper.Read(buf, LF); // get rid of line feed + transaction.Timestamp = _mDateHelper.Parse(timestamp); - if (parent != null) - { - parent.AddChild(evt); - return parent; - } - return evt; - } - if (identifier == 'H') - { - IMessage heartbeat = new DefaultHeartbeat(type, name); - String status0 = helper.Read(buf, TAB); - String data1 = helper.ReadRaw(buf, TAB); + if (parent != null) + { + parent.AddChild(transaction); + } - helper.Read(buf, LF); // get rid of line feed - heartbeat.Timestamp = _mDateHelper.Parse(timestamp); - heartbeat.Status = status0; - heartbeat.AddData(data1); + stack.Push(parent); + return transaction; + case 'A': + DefaultTransaction tran = new DefaultTransaction(type, name, null); + String status = helper.Read(buf, TAB); + String duration = helper.Read(buf, TAB); + String data = helper.ReadRaw(buf, TAB); - if (parent != null) - { - parent.AddChild(heartbeat); - return parent; - } - return heartbeat; - } - if (identifier == 't') - { - IMessage transaction = new DefaultTransaction(type, name, - null); + helper.Read(buf, LF); // get rid of line feed + tran.Timestamp = _mDateHelper.Parse(timestamp); + tran.Status = status; + tran.AddData(data); - helper.Read(buf, LF); // get rid of line feed - transaction.Timestamp = _mDateHelper.Parse(timestamp); + long d = Int64.Parse(duration.Substring(0, duration.Length - 2), NumberStyles.Integer); - if (parent != null) - { - parent.AddChild(transaction); - } + tran.DurationInMicros = d; - stack.Push(parent); - return transaction; - } - if (identifier == 'A') - { - ITransaction transaction2 = new DefaultTransaction(type, name, null); - String status3 = helper.Read(buf, TAB); - String duration = helper.Read(buf, TAB); - String data4 = helper.ReadRaw(buf, TAB); + if (parent != null) + { + parent.AddChild(tran); + return parent; + } + return tran; + case 'T': + String transactionStatus = helper.Read(buf, TAB); + String transactionDuration = helper.Read(buf, TAB); + String transactionData = helper.ReadRaw(buf, TAB); - helper.Read(buf, LF); // get rid of line feed - transaction2.Timestamp = _mDateHelper.Parse(timestamp); - transaction2.Status = status3; - transaction2.AddData(data4); + helper.Read(buf, LF); // get rid of line feed + parent.Status = transactionStatus; + parent.AddData(transactionData); - long d = Int64.Parse(duration.Substring(0, duration.Length - 2), NumberStyles.Integer); - transaction2.DurationInMicros = d; + long transactionD = Int64.Parse(transactionDuration.Substring(0, transactionDuration.Length - 2), NumberStyles.Integer); - if (parent != null) - { - parent.AddChild(transaction2); - return parent; - } - return transaction2; - } - if (identifier == 'T') - { - String status5 = helper.Read(buf, TAB); - String duration6 = helper.Read(buf, TAB); - String data7 = helper.ReadRaw(buf, TAB); + parent.DurationInMicros = transactionD; - helper.Read(buf, LF); // get rid of line feed - parent.Status = status5; - parent.AddData(data7); + return stack.Pop(); + case 'E': + DefaultEvent evt = new DefaultEvent(type, name); + String eventStatus = helper.Read(buf, TAB); + String eventData = helper.ReadRaw(buf, TAB); - long d8 = Int64.Parse( - duration6.Substring(0, duration6.Length - 2), - NumberStyles.Integer); - parent.DurationInMicros = d8; + helper.Read(buf, LF); // get rid of line feed + evt.Timestamp = _mDateHelper.Parse(timestamp); + evt.Status = eventStatus; + evt.AddData(eventData); - return stack.Pop(); + if (parent != null) + { + parent.AddChild(evt); + return parent; + } + return evt; + case 'M': + DefaultMetric metric = new DefaultMetric(type, name); + String metricStatus = helper.Read(buf, TAB); + String metricData = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); + metric.Timestamp = _mDateHelper.Parse(timestamp); + metric.Status = metricStatus; + metric.AddData(metricData); + + if (parent != null) + { + parent.AddChild(metric); + return parent; + } + return metric; + case 'H': + DefaultHeartbeat heartbeat = new DefaultHeartbeat(type, name); + String heartbeatStatus = helper.Read(buf, TAB); + String heartbeatData = helper.ReadRaw(buf, TAB); + + helper.Read(buf, LF); // get rid of line feed + heartbeat.Timestamp = _mDateHelper.Parse(timestamp); + heartbeat.Status = heartbeatStatus; + heartbeat.AddData(heartbeatData); + + if (parent != null) + { + parent.AddChild(heartbeat); + return parent; + } + return heartbeat; } + Logger.Error("Unknown identifier(" + identifier + ") of message: " + buf); + //throw new Exception("Unknown identifier int name"); //java版的抛出异常 // unknown message, ignore it return parent; @@ -320,16 +329,12 @@ protected internal int EncodeLine(IMessage message, ChannelBuffer buf, char type public int EncodeMessage(IMessage message, ChannelBuffer buf) { - if (message is IEvent) - { - return EncodeLine(message, buf, 'E', Policy.DEFAULT); - } - var transaction = message as ITransaction; - if (transaction != null) + if (message is ITransaction) { + var transaction = message as ITransaction; IList children = transaction.Children; - if ((children.Count == 0)) + if (children.Count == 0) { return EncodeLine(transaction, buf, 'A', Policy.WITH_DURATION); } @@ -349,11 +354,19 @@ public int EncodeMessage(IMessage message, ChannelBuffer buf) return count; } + if (message is IEvent) + { + return EncodeLine(message, buf, 'E', Policy.DEFAULT); + } if (message is IHeartbeat) { return EncodeLine(message, buf, 'H', Policy.DEFAULT); } - throw new Exception("Unsupported message type: " + message.Type + "."); + if (message is IMetric) + { + return EncodeLine(message, buf, 'M', Policy.DEFAULT); + } + throw new Exception(string.Format("Unsupported message type: {0}.", message.Type)); } #region Nested type: BufferHelper diff --git a/Message/Spi/IMessageProducer.cs b/Message/Spi/IMessageProducer.cs index c7152d6..655756c 100644 --- a/Message/Spi/IMessageProducer.cs +++ b/Message/Spi/IMessageProducer.cs @@ -83,6 +83,8 @@ namespace Com.Dianping.Cat.Message.Spi /// public interface IMessageProducer { + String CreateMessageId(); + /// /// Log an error. /// @@ -107,6 +109,14 @@ public interface IMessageProducer /// name value pairs in the format of "a=1&b=2&..." void LogHeartbeat(String type, String name, String status, String nameValuePairs); + /// + /// Log a metric in one shot. + /// + /// metric name + /// "0" means success, otherwise means error code + /// name value pairs in the format of "a=1&b=2&..." + void LogMetric(String name, String status, String nameValuePairs); + /// /// Create a new event with given type and name. /// @@ -128,6 +138,12 @@ public interface IMessageProducer /// transaction name ITransaction NewTransaction(String type, String name); - String CreateMessageId(); + /// + /// Create a new metric with given type and name. + /// + /// metric type + /// metric name + IMetric NewMetric(String type, String name); + } } \ No newline at end of file diff --git a/Message/Spi/Internals/DefaultMessageProducer.cs b/Message/Spi/Internals/DefaultMessageProducer.cs index e2e48cd..1622f59 100644 --- a/Message/Spi/Internals/DefaultMessageProducer.cs +++ b/Message/Spi/Internals/DefaultMessageProducer.cs @@ -77,6 +77,20 @@ public virtual void LogHeartbeat(String type, String name, String status, String heartbeat.Complete(); } + public virtual void LogMetric(String name, String status, String nameValuePairs) + { + String type = string.Empty; + IMetric metric = NewMetric(type, name); + + if (!string.IsNullOrWhiteSpace(nameValuePairs)) + { + metric.AddData(nameValuePairs); + } + + metric.Status = status; + metric.Complete(); + } + public virtual IEvent NewEvent(String type, String name) { if (!_mManager.HasContext()) @@ -129,6 +143,30 @@ public virtual ITransaction NewTransaction(String type, String name) return new NullTransaction(); } + /// + /// new metric + /// + /// group + /// key + public virtual IMetric NewMetric(String type, String name) + { + // this enable CAT client logging cat message without explicit setup + if (!_mManager.HasContext()) + { + _mManager.Setup(); + } + + if (_mManager.CatEnabled) + { + IMetric metric = new DefaultMetric(string.IsNullOrWhiteSpace(type) ? string.Empty : type, name); + + _mManager.Add(metric); + return metric; + } + return new NullMetric(); + } + #endregion + } } \ No newline at end of file diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 3686002..4e55a56 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -24,7 +24,7 @@ public static ITransaction BeginClientTransaction(string type, string name, WebR { var tran = Cat.GetProducer().NewTransaction(type, name); tran.Status = "0"; - Cat.GetProducer().LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); + LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { @@ -34,7 +34,7 @@ public static ITransaction BeginClientTransaction(string type, string name, WebR string serverMessageId = Cat.GetProducer().CreateMessageId(); string rootMessageId = (tree.RootMessageId ?? tree.MessageId); string currentMessageId = tree.MessageId; - Cat.GetProducer().LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); + LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); request.Headers.Add(CatHelper.CatRootId, rootMessageId); request.Headers.Add(CatHelper.CatParentId, currentMessageId); @@ -118,11 +118,45 @@ public static ITransaction BeginTransaction(string type, string name) } } - public static void AddLogEvent(string type, string name) + public static void LogEvent(string type, string name, string status = "0", string nameValuePairs = null) { if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) return; - Cat.GetProducer().LogEvent(type, name, "0", null); + Cat.GetProducer().LogEvent(type, name, status, nameValuePairs); + } + + public static void LogError(Exception ex) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return; + Cat.GetProducer().LogError(ex); + } + + public static void LogHeartbeat(string type, string name, string status = "0", string nameValuePairs = null) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return; + Cat.GetProducer().LogHeartbeat(type, name, status, nameValuePairs); + } + + public static void LogMetricForCount(string name, int quantity = 1) + { + LogMetricInternal(name, "C", quantity.ToString()); + } + + public static void LogMetricForDuration(string name, double value) + { + LogMetricInternal(name, "T", String.Format("{0:F}", value)); + } + + public static void LogMetricForSum(string name, double value) + { + LogMetricInternal(name, "S", String.Format("{0:F}", value)); + } + + public static void LogMetricForSum(string name, double sum, int quantity) + { + LogMetricInternal(name, "S,C", String.Format("{0},{1:F}", sum, quantity)); } public static string GetRootMessageId() @@ -181,6 +215,14 @@ public static void CompleteTrancation(ITransaction tran) } } + private static void LogMetricInternal(string name, string status, string keyValuePairs = null) + { + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return; + Cat.GetProducer().LogMetric(name, status, keyValuePairs); + } + + #region url info private static string getURLServerValue(HttpRequest request) { if (request == null) @@ -217,5 +259,7 @@ private static string getServerVariables(this HttpRequest request, string key) var headerKey = request.ServerVariables.AllKeys.Where(p => p.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); return request.ServerVariables[headerKey] ?? "null"; } + #endregion + } } diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index ed0fc6c..d258302 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -26,16 +26,21 @@ public void ProcessRequest(HttpContext context) try { handler.ProcessRequest(context); + CatHelper.CompleteTrancation(tran); } catch (Exception ex) { var baseEx = ex.GetBaseException(); + if (baseEx is ThreadAbortException) + { + CatHelper.CompleteTrancation(tran); + return; + } + CatHelper.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); CatHelper.SetTrancationStatus(tran, baseEx); - CatHelper.AddLogEvent("Exception", baseEx.StackTrace); CatHelper.CompleteTrancation(tran); - throw ex; + throw; } - CatHelper.CompleteTrancation(tran); } public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index eee557a..17e2467 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -26,16 +26,21 @@ public void ProcessRequest(HttpContext context) try { handler.ProcessRequest(context); + CatHelper.CompleteTrancation(tran); } catch (Exception ex) { var baseEx = ex.GetBaseException(); + if (baseEx is ThreadAbortException) + { + CatHelper.CompleteTrancation(tran); + return; + } + CatHelper.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); CatHelper.SetTrancationStatus(tran, baseEx); - CatHelper.AddLogEvent("Exception", baseEx.StackTrace); CatHelper.CompleteTrancation(tran); - throw ex; + throw; } - CatHelper.CompleteTrancation(tran); } } } From 61541d8b28e1c36f8b3f1d770fe4b25436b3117f Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 16 Jan 2015 14:10:46 +0800 Subject: [PATCH 06/21] =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Util/CatHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 4e55a56..49260db 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -156,7 +156,7 @@ public static void LogMetricForSum(string name, double value) public static void LogMetricForSum(string name, double sum, int quantity) { - LogMetricInternal(name, "S,C", String.Format("{0},{1:F}", sum, quantity)); + LogMetricInternal(name, "S,C", String.Format("{0},{1:F}", quantity, sum)); } public static string GetRootMessageId() From a34d42b5694748e33a2a21986cb5894b7cf991fa Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 16 Jan 2015 14:11:10 +0800 Subject: [PATCH 07/21] lost csproj --- Cat.Net.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cat.Net.csproj b/Cat.Net.csproj index 1157987..8e9e5ce 100644 --- a/Cat.Net.csproj +++ b/Cat.Net.csproj @@ -50,6 +50,9 @@ + + + From 9905d18d89ea2777446f434a7a2e88e72acb88ab Mon Sep 17 00:00:00 2001 From: chinaboard Date: Mon, 19 Jan 2015 11:28:21 +0800 Subject: [PATCH 08/21] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cat.cs | 82 ++++++++++++++++++++++++++++++++++++++++------- Util/CatHelper.cs | 40 +++++++---------------- 2 files changed, 82 insertions(+), 40 deletions(-) diff --git a/Cat.cs b/Cat.cs index ec50e39..8e4369c 100644 --- a/Cat.cs +++ b/Cat.cs @@ -1,7 +1,9 @@ using Com.Dianping.Cat.Configuration; +using Com.Dianping.Cat.Message; using Com.Dianping.Cat.Message.Spi; using Com.Dianping.Cat.Message.Spi.Internals; using Com.Dianping.Cat.Util; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -33,14 +35,10 @@ public static IMessageProducer GetProducer() return Instance._mProducer; } - public static void Initialize() - { - var path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "App_Data\\TCConfig\\CatConfig.xml"); - Initialize(path); - } - - public static void Initialize(string configFile) + public static void Initialize(string configFile = null) { + if (string.IsNullOrWhiteSpace(configFile)) + configFile = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "App_Data\\TCConfig\\CatConfig.xml"); if (Instance._mInitialized) { Logger.Warn("Cat can't initialize again with config file(%s), IGNORED!", configFile); @@ -69,6 +67,65 @@ public static bool IsInitialized() return isInitialized; } + #region Log + + public static void LogError(Exception ex) + { + Cat.GetProducer().LogError(ex); + } + + public static void LogEvent(string type, string name, string status = "0", string nameValuePairs = null) + { + Cat.GetProducer().LogEvent(type, name, status, nameValuePairs); + } + + public static void LogHeartbeat(string type, string name, string status = "0", string nameValuePairs = null) + { + Cat.GetProducer().LogHeartbeat(type, name, status, nameValuePairs); + } + + public static void LogMetricForCount(string name, int quantity = 1) + { + LogMetricInternal(name, "C", quantity.ToString()); + } + + public static void LogMetricForDuration(string name, double value) + { + LogMetricInternal(name, "T", String.Format("{0:F}", value)); + } + + public static void LogMetricForSum(string name, double sum, int quantity) + { + LogMetricInternal(name, "S,C", String.Format("{0},{1:F}", quantity, sum)); + } + + private static void LogMetricInternal(string name, string status, string keyValuePairs = null) + { + Cat.GetProducer().LogMetric(name, status, keyValuePairs); + } + + #endregion + + #region New message + + public static IEvent NewEvent(string type, string name) + { + return Cat.GetProducer().NewEvent(type, name); + } + + public static IHeartbeat Neweartbeat(string type,string name) + { + return Cat.GetProducer().NewHeartbeat(type, name); + } + + public static ITransaction NewTransaction(string type, string name) + { + return Cat.GetProducer().NewTransaction(type, name); + } + #endregion + + #region 配置文件属性获取 + private static ClientConfig LoadClientConfig(string configFile) { ClientConfig config = new ClientConfig(); @@ -118,11 +175,11 @@ private static Domain BuildDomain(XmlNodeList nodes) XmlElement node = (XmlElement)nodes[0]; return new Domain - { - Id = GetStringProperty(node, "id", "Unknown"), - //Ip = GetStringProperty(node, "ip", null), - Enabled = GetBooleanProperty(node, "enabled", false) - }; + { + Id = GetStringProperty(node, "id", "Unknown"), + //Ip = GetStringProperty(node, "ip", null), + Enabled = GetBooleanProperty(node, "enabled", false) + }; } private static IEnumerable BuildServers(XmlNodeList nodes) @@ -200,6 +257,7 @@ private static int GetIntProperty(XmlElement element, string name, int defaultVa return defaultValue; } + #endregion } } \ No newline at end of file diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 49260db..921b112 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -108,7 +108,7 @@ public static ITransaction BeginTransaction(string type, string name) return null; try { - var tran = Cat.GetProducer().NewTransaction(type, name); + var tran = Cat.NewTransaction(type, name); tran.Status = "0"; return tran; } @@ -120,49 +120,40 @@ public static ITransaction BeginTransaction(string type, string name) public static void LogEvent(string type, string name, string status = "0", string nameValuePairs = null) { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return; - Cat.GetProducer().LogEvent(type, name, status, nameValuePairs); + Cat.LogEvent(type, name, status, nameValuePairs); } public static void LogError(Exception ex) { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return; - Cat.GetProducer().LogError(ex); + Cat.LogError(ex); } public static void LogHeartbeat(string type, string name, string status = "0", string nameValuePairs = null) { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return; - Cat.GetProducer().LogHeartbeat(type, name, status, nameValuePairs); + Cat.LogHeartbeat(type, name, status, nameValuePairs); } public static void LogMetricForCount(string name, int quantity = 1) { - LogMetricInternal(name, "C", quantity.ToString()); + Cat.LogMetricForCount(name, quantity); } public static void LogMetricForDuration(string name, double value) { - LogMetricInternal(name, "T", String.Format("{0:F}", value)); + Cat.LogMetricForDuration(name, value); } - public static void LogMetricForSum(string name, double value) + public static void LogMetricForSum(string name, double sum, int quantity = 1) { - LogMetricInternal(name, "S", String.Format("{0:F}", value)); - } - - public static void LogMetricForSum(string name, double sum, int quantity) - { - LogMetricInternal(name, "S,C", String.Format("{0},{1:F}", quantity, sum)); + Cat.LogMetricForSum(name, sum, quantity); } public static string GetRootMessageId() { - if (!Cat.IsInitialized()) { return string.Empty; } - if (!Cat.GetManager().CatEnabled) { return string.Empty; } + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + { + return string.Empty; + } var tree = Cat.GetManager().ThreadLocalMessageTree; if (tree == null) { @@ -215,13 +206,6 @@ public static void CompleteTrancation(ITransaction tran) } } - private static void LogMetricInternal(string name, string status, string keyValuePairs = null) - { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return; - Cat.GetProducer().LogMetric(name, status, keyValuePairs); - } - #region url info private static string getURLServerValue(HttpRequest request) { From 4dd28f28f51d269462e2c12bc53efd4bc6bb849e Mon Sep 17 00:00:00 2001 From: chinaboard Date: Mon, 19 Jan 2015 11:42:57 +0800 Subject: [PATCH 09/21] =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cat.cs b/Cat.cs index 8e4369c..249308b 100644 --- a/Cat.cs +++ b/Cat.cs @@ -113,7 +113,7 @@ public static IEvent NewEvent(string type, string name) return Cat.GetProducer().NewEvent(type, name); } - public static IHeartbeat Neweartbeat(string type,string name) + public static IHeartbeat NewHeartbeat(string type,string name) { return Cat.GetProducer().NewHeartbeat(type, name); } From 2e949c5019426dd21a3b21d839add410b4d2f12a Mon Sep 17 00:00:00 2001 From: chinaboard Date: Mon, 19 Jan 2015 13:44:25 +0800 Subject: [PATCH 10/21] =?UTF-8?q?helper=E5=92=8Chandler=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Util/CatHelper.cs | 191 ++++++++++--------------------------- Web/CatHttpAsyncHandler.cs | 10 +- Web/CatHttpHandler.cs | 10 +- 3 files changed, 59 insertions(+), 152 deletions(-) diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 921b112..5157e83 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -18,134 +18,70 @@ public static class CatHelper public static ITransaction BeginClientTransaction(string type, string name, WebRequest request) { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return null; - try - { - var tran = Cat.GetProducer().NewTransaction(type, name); - tran.Status = "0"; - LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - string serverMessageId = Cat.GetProducer().CreateMessageId(); - string rootMessageId = (tree.RootMessageId ?? tree.MessageId); - string currentMessageId = tree.MessageId; - LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); - - request.Headers.Add(CatHelper.CatRootId, rootMessageId); - request.Headers.Add(CatHelper.CatParentId, currentMessageId); - request.Headers.Add(CatHelper.CatId, serverMessageId); - - return tran; - } - catch + var tran = Cat.NewTransaction(type, name); + tran.Status = "0"; + Cat.LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) { - return null; + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; } + string serverMessageId = Cat.GetProducer().CreateMessageId(); + string rootMessageId = (tree.RootMessageId ?? tree.MessageId); + string currentMessageId = tree.MessageId; + Cat.LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); + + request.Headers.Add(CatHelper.CatRootId, rootMessageId); + request.Headers.Add(CatHelper.CatParentId, currentMessageId); + request.Headers.Add(CatHelper.CatId, serverMessageId); + + return tran; } public static ITransaction BeginServerTransaction(string type, string name = null, HttpResponse response = null) { + var httpContext = System.Web.HttpContext.Current; + if (httpContext == null) { return null; } + var request = httpContext.Request; - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return null; - try - { - var httpContext = System.Web.HttpContext.Current; - if (httpContext == null) { return null; } - var request = httpContext.Request; - - string rootMessageId = request.Headers[CatHelper.CatRootId]; - string serverMessageId = request.Headers[CatHelper.CatParentId]; - string currentMessageId = request.Headers[CatHelper.CatId]; - - if (string.IsNullOrWhiteSpace(name)) - name = request.Path; - - var tran = Cat.GetProducer().NewTransaction(type, name); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - - tree.RootMessageId = rootMessageId; - tree.ParentMessageId = serverMessageId; - if (!string.IsNullOrEmpty(currentMessageId)) - { - tree.MessageId = currentMessageId; - } - tran.Status = "0"; - - if (response != null) - { - if (!string.IsNullOrWhiteSpace(rootMessageId)) - response.AddHeader(CatHelper.CatRootId, rootMessageId); - if (!string.IsNullOrWhiteSpace(currentMessageId)) - response.AddHeader(CatHelper.CatParentId, currentMessageId); - response.AddHeader(CatHelper.CatId, tree.MessageId); - } - - Cat.GetProducer().LogEvent(type, type + ".Server", "0", getURLServerValue(request)); - Cat.GetProducer().LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); - Cat.GetProducer().LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); - return tran; - } - catch + string rootMessageId = request.Headers[CatHelper.CatRootId]; + string serverMessageId = request.Headers[CatHelper.CatParentId]; + string currentMessageId = request.Headers[CatHelper.CatId]; + + if (string.IsNullOrWhiteSpace(name)) + name = request.Path; + + var tran = Cat.NewTransaction(type, name); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) { - return null; + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; } - } - public static ITransaction BeginTransaction(string type, string name) - { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return null; - try + tree.RootMessageId = rootMessageId; + tree.ParentMessageId = serverMessageId; + if (!string.IsNullOrEmpty(currentMessageId)) { - var tran = Cat.NewTransaction(type, name); - tran.Status = "0"; - return tran; + tree.MessageId = currentMessageId; } - catch + tran.Status = "0"; + + if (response != null) { - return null; + if (!string.IsNullOrWhiteSpace(rootMessageId)) + response.AddHeader(CatHelper.CatRootId, rootMessageId); + if (!string.IsNullOrWhiteSpace(currentMessageId)) + response.AddHeader(CatHelper.CatParentId, currentMessageId); + response.AddHeader(CatHelper.CatId, tree.MessageId); } - } - public static void LogEvent(string type, string name, string status = "0", string nameValuePairs = null) - { - Cat.LogEvent(type, name, status, nameValuePairs); - } + Cat.LogEvent(type, type + ".Server", "0", getURLServerValue(request)); + Cat.LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); + Cat.LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); - public static void LogError(Exception ex) - { - Cat.LogError(ex); - } - - public static void LogHeartbeat(string type, string name, string status = "0", string nameValuePairs = null) - { - Cat.LogHeartbeat(type, name, status, nameValuePairs); - } - - public static void LogMetricForCount(string name, int quantity = 1) - { - Cat.LogMetricForCount(name, quantity); - } - - public static void LogMetricForDuration(string name, double value) - { - Cat.LogMetricForDuration(name, value); - } - - public static void LogMetricForSum(string name, double sum, int quantity = 1) - { - Cat.LogMetricForSum(name, sum, quantity); + return tran; } public static string GetRootMessageId() @@ -161,12 +97,7 @@ public static string GetRootMessageId() tree = Cat.GetManager().ThreadLocalMessageTree; } - string rootId = tree.RootMessageId; - if (string.IsNullOrEmpty(rootId)) - { - rootId = tree.MessageId; - } - return rootId; + return string.IsNullOrEmpty(tree.RootMessageId) ? tree.MessageId : tree.RootMessageId; } public static string GetMessageId() @@ -182,30 +113,6 @@ public static string GetMessageId() return tree.MessageId; } - public static void SetTrancationStatus(ITransaction tran, string status) - { - if (tran != null) - { - tran.Status = status; - } - } - - public static void SetTrancationStatus(ITransaction tran, Exception exception) - { - if (tran != null) - { - tran.SetStatus(exception); - } - } - - public static void CompleteTrancation(ITransaction tran) - { - if (tran != null) - { - tran.Complete(); - } - } - #region url info private static string getURLServerValue(HttpRequest request) { diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index d258302..bc380b6 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -26,19 +26,19 @@ public void ProcessRequest(HttpContext context) try { handler.ProcessRequest(context); - CatHelper.CompleteTrancation(tran); + tran.Complete(); } catch (Exception ex) { var baseEx = ex.GetBaseException(); if (baseEx is ThreadAbortException) { - CatHelper.CompleteTrancation(tran); + tran.Complete(); return; } - CatHelper.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); - CatHelper.SetTrancationStatus(tran, baseEx); - CatHelper.CompleteTrancation(tran); + Cat.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); + tran.SetStatus(baseEx); + tran.Complete(); throw; } } diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index 17e2467..17c41bb 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -26,19 +26,19 @@ public void ProcessRequest(HttpContext context) try { handler.ProcessRequest(context); - CatHelper.CompleteTrancation(tran); + tran.Complete(); } catch (Exception ex) { var baseEx = ex.GetBaseException(); if (baseEx is ThreadAbortException) { - CatHelper.CompleteTrancation(tran); + tran.Complete(); return; } - CatHelper.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); - CatHelper.SetTrancationStatus(tran, baseEx); - CatHelper.CompleteTrancation(tran); + Cat.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); + tran.SetStatus(baseEx); + tran.Complete(); throw; } } From aa2a3c68dc535eb09a20fc0025991628221cc144 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Wed, 28 Jan 2015 17:16:11 +0800 Subject: [PATCH 11/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttpasynchandler?= =?UTF-8?q?=E4=B8=8D=E8=AE=B0=E5=BD=95cat=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 67 ++++++++++++++++++++++++++------------ Web/CatHttpHandler.cs | 22 +++++++------ 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index bc380b6..f88d22c 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -6,51 +6,78 @@ using System.Web.SessionState; using Com.Dianping.Cat.Util; using System.Threading; +using Com.Dianping.Cat.Message; namespace Com.Dianping.Cat.Web { public class CatHttpAsyncHandler : IHttpAsyncHandler, IRequiresSessionState { + private ITransaction tran = null; + private IHttpAsyncHandler asyncHandler; + + public bool IsReusable { get { return asyncHandler.IsReusable; } } + public CatHttpAsyncHandler(IHttpAsyncHandler asyncHandler) { - this.handler = asyncHandler; + this.asyncHandler = asyncHandler; } - public IHttpAsyncHandler handler; + public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) + { + if (asyncHandler == null) + return null; + tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + try + { + return asyncHandler.BeginProcessRequest(context, cb, extraData); + } + catch (Exception ex) + { + CatHelper.SetTrancationStatus(tran, ex); + throw; + } + } - public bool IsReusable { get { return handler.IsReusable; } } + public void EndProcessRequest(IAsyncResult result) + { + if (asyncHandler == null) + return; + try + { + asyncHandler.EndProcessRequest(result); + } + catch (Exception ex) + { + CatHelper.SetTrancationStatus(tran, ex); + throw; + } + finally + { + CatHelper.CompleteTrancation(tran); + } + } public void ProcessRequest(HttpContext context) { - var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { - handler.ProcessRequest(context); - tran.Complete(); + asyncHandler.ProcessRequest(context); } catch (Exception ex) { var baseEx = ex.GetBaseException(); if (baseEx is ThreadAbortException) { - tran.Complete(); return; } - Cat.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); - tran.SetStatus(baseEx); - tran.Complete(); + CatHelper.SetTrancationStatus(tran, baseEx); throw; } - } - - public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) - { - return handler.BeginProcessRequest(context, cb, extraData); - } - - public void EndProcessRequest(IAsyncResult result) - { - handler.EndProcessRequest(result); + finally + { + CatHelper.CompleteTrancation(tran); + } } } } diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index 17c41bb..5bf6e2c 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -6,41 +6,43 @@ using System.Web.SessionState; using Com.Dianping.Cat.Util; using System.Threading; +using Com.Dianping.Cat.Message; namespace Com.Dianping.Cat.Web { public class CatHttpHandler : IHttpHandler, IRequiresSessionState { + private ITransaction tran = null; + private IHttpHandler handler; + + public bool IsReusable { get { return handler.IsReusable; } } + public CatHttpHandler(IHttpHandler httpHandler) { this.handler = httpHandler; } - private IHttpHandler handler; - - public bool IsReusable { get { return handler.IsReusable; } } - public void ProcessRequest(HttpContext context) { - var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { handler.ProcessRequest(context); - tran.Complete(); } catch (Exception ex) { var baseEx = ex.GetBaseException(); if (baseEx is ThreadAbortException) { - tran.Complete(); return; } - Cat.LogEvent(tran.Type, "Exception", baseEx.GetType().FullName, baseEx.StackTrace); - tran.SetStatus(baseEx); - tran.Complete(); + CatHelper.SetTrancationStatus(tran, baseEx); throw; } + finally + { + CatHelper.CompleteTrancation(tran); + } } } } From 3ad30f8f1697322a5babb4a05fe848848c486da5 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Wed, 28 Jan 2015 17:44:38 +0800 Subject: [PATCH 12/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 12 +++++------- Web/CatHttpHandler.cs | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index f88d22c..77a6051 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -33,27 +33,25 @@ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, o } catch (Exception ex) { - CatHelper.SetTrancationStatus(tran, ex); + tran.SetStatus(ex); throw; } } public void EndProcessRequest(IAsyncResult result) { - if (asyncHandler == null) - return; try { asyncHandler.EndProcessRequest(result); } catch (Exception ex) { - CatHelper.SetTrancationStatus(tran, ex); + tran.SetStatus(ex); throw; } finally { - CatHelper.CompleteTrancation(tran); + tran.Complete(); } } @@ -71,12 +69,12 @@ public void ProcessRequest(HttpContext context) { return; } - CatHelper.SetTrancationStatus(tran, baseEx); + tran.SetStatus(ex); throw; } finally { - CatHelper.CompleteTrancation(tran); + tran.Complete(); } } } diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index 5bf6e2c..bc6836a 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -36,12 +36,12 @@ public void ProcessRequest(HttpContext context) { return; } - CatHelper.SetTrancationStatus(tran, baseEx); + tran.SetStatus(ex); throw; } finally { - CatHelper.CompleteTrancation(tran); + tran.Complete(); } } } From c6ae97eceef92abc4d31966c076c70ea3c72caca Mon Sep 17 00:00:00 2001 From: chinaboard Date: Wed, 28 Jan 2015 17:45:40 +0800 Subject: [PATCH 13/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AFretur?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index 77a6051..5023d67 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -24,8 +24,6 @@ public CatHttpAsyncHandler(IHttpAsyncHandler asyncHandler) public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { - if (asyncHandler == null) - return null; tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { From f7fdbbed5e49eeeeaeb278edbc311bd708b5a215 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Thu, 29 Jan 2015 10:25:35 +0800 Subject: [PATCH 14/21] =?UTF-8?q?=E5=BC=82=E6=AD=A5handler=E9=87=8C?= =?UTF-8?q?=E5=8F=AA=E6=9C=89endprocessrequest=E5=87=BA=E7=8E=B0=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=97=B6=E6=89=8D=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index 5023d67..81df1f3 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -12,7 +12,6 @@ namespace Com.Dianping.Cat.Web { public class CatHttpAsyncHandler : IHttpAsyncHandler, IRequiresSessionState { - private ITransaction tran = null; private IHttpAsyncHandler asyncHandler; public bool IsReusable { get { return asyncHandler.IsReusable; } } @@ -24,7 +23,7 @@ public CatHttpAsyncHandler(IHttpAsyncHandler asyncHandler) public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { - tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { return asyncHandler.BeginProcessRequest(context, cb, extraData); @@ -32,12 +31,14 @@ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, o catch (Exception ex) { tran.SetStatus(ex); + tran.Complete(); throw; } } public void EndProcessRequest(IAsyncResult result) { + var tran = CatHelper.BeginServerTransaction("URL"); try { asyncHandler.EndProcessRequest(result); @@ -45,17 +46,14 @@ public void EndProcessRequest(IAsyncResult result) catch (Exception ex) { tran.SetStatus(ex); - throw; - } - finally - { tran.Complete(); + throw; } } public void ProcessRequest(HttpContext context) { - tran = CatHelper.BeginServerTransaction("URL", response: context.Response); + var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { asyncHandler.ProcessRequest(context); From ce2195c2d1bdaca35fd1df3b65758fe7521b0916 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Thu, 29 Jan 2015 11:32:51 +0800 Subject: [PATCH 15/21] =?UTF-8?q?=E4=BD=BF=E7=94=A8AsyncState=E8=A7=A3?= =?UTF-8?q?=E5=86=B3tran=E7=9A=84=E7=BA=BF=E7=A8=8B=E9=97=B4=E4=BC=A0?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index 81df1f3..a07f25c 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -26,29 +26,37 @@ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, o var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); try { + if (extraData == null) + extraData = new ExtraData(extraData, tran); return asyncHandler.BeginProcessRequest(context, cb, extraData); } catch (Exception ex) { tran.SetStatus(ex); - tran.Complete(); throw; } } public void EndProcessRequest(IAsyncResult result) { - var tran = CatHelper.BeginServerTransaction("URL"); + ITransaction tran = null; try { + var extraData = result.AsyncState as ExtraData; + if (extraData != null) + tran = extraData.CatTransaction; + asyncHandler.EndProcessRequest(result); } catch (Exception ex) { tran.SetStatus(ex); - tran.Complete(); throw; } + finally + { + tran.Complete(); + } } public void ProcessRequest(HttpContext context) @@ -73,5 +81,16 @@ public void ProcessRequest(HttpContext context) tran.Complete(); } } + + class ExtraData + { + public object Value { get; set; } + public ITransaction CatTransaction { get; set; } + public ExtraData(object value, ITransaction tran) + { + Value = value; + CatTransaction = tran; + } + } } } From e088d4681856518901de36878de2079307d49c8b Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 30 Jan 2015 20:31:43 +0800 Subject: [PATCH 16/21] =?UTF-8?q?=E4=BD=BF=E7=94=A8logerror=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=BC=82=E5=B8=B8=EF=BC=8C=E8=B0=83=E6=95=B4AsyncStat?= =?UTF-8?q?e=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/CatHttpAsyncHandler.cs | 24 ++++++++---------------- Web/CatHttpHandler.cs | 1 + 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs index a07f25c..68db53d 100644 --- a/Web/CatHttpAsyncHandler.cs +++ b/Web/CatHttpAsyncHandler.cs @@ -27,11 +27,13 @@ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, o try { if (extraData == null) - extraData = new ExtraData(extraData, tran); + extraData = tran; + return asyncHandler.BeginProcessRequest(context, cb, extraData); } catch (Exception ex) { + Cat.GetProducer().LogError(ex); tran.SetStatus(ex); throw; } @@ -42,14 +44,15 @@ public void EndProcessRequest(IAsyncResult result) ITransaction tran = null; try { - var extraData = result.AsyncState as ExtraData; + var extraData = result.AsyncState as ITransaction; if (extraData != null) - tran = extraData.CatTransaction; + tran = extraData; asyncHandler.EndProcessRequest(result); } catch (Exception ex) { + Cat.GetProducer().LogError(ex); tran.SetStatus(ex); throw; } @@ -68,11 +71,11 @@ public void ProcessRequest(HttpContext context) } catch (Exception ex) { - var baseEx = ex.GetBaseException(); - if (baseEx is ThreadAbortException) + if (ex.GetBaseException() is ThreadAbortException) { return; } + Cat.GetProducer().LogError(ex); tran.SetStatus(ex); throw; } @@ -81,16 +84,5 @@ public void ProcessRequest(HttpContext context) tran.Complete(); } } - - class ExtraData - { - public object Value { get; set; } - public ITransaction CatTransaction { get; set; } - public ExtraData(object value, ITransaction tran) - { - Value = value; - CatTransaction = tran; - } - } } } diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs index bc6836a..3f973ab 100644 --- a/Web/CatHttpHandler.cs +++ b/Web/CatHttpHandler.cs @@ -36,6 +36,7 @@ public void ProcessRequest(HttpContext context) { return; } + Cat.GetProducer().LogError(ex); tran.SetStatus(ex); throw; } From b4fd57a90dc8c026eb9e899a4d598bee646133eb Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 30 Jan 2015 20:47:59 +0800 Subject: [PATCH 17/21] update BeginServerTransaction --- Util/CatHelper.cs | 82 ++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 5157e83..05a865f 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -41,47 +41,55 @@ public static ITransaction BeginClientTransaction(string type, string name, WebR public static ITransaction BeginServerTransaction(string type, string name = null, HttpResponse response = null) { - var httpContext = System.Web.HttpContext.Current; - if (httpContext == null) { return null; } - var request = httpContext.Request; - - string rootMessageId = request.Headers[CatHelper.CatRootId]; - string serverMessageId = request.Headers[CatHelper.CatParentId]; - string currentMessageId = request.Headers[CatHelper.CatId]; - - if (string.IsNullOrWhiteSpace(name)) - name = request.Path; - - var tran = Cat.NewTransaction(type, name); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - - tree.RootMessageId = rootMessageId; - tree.ParentMessageId = serverMessageId; - if (!string.IsNullOrEmpty(currentMessageId)) + if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) + return null; + try { - tree.MessageId = currentMessageId; + var httpContext = System.Web.HttpContext.Current; + if (httpContext == null) { return null; } + var request = httpContext.Request; + + string rootMessageId = request.Headers[CatHelper.CatRootId]; + string serverMessageId = request.Headers[CatHelper.CatParentId]; + string currentMessageId = request.Headers[CatHelper.CatId]; + + if (string.IsNullOrWhiteSpace(name)) + name = request.Path; + + var tran = Cat.GetProducer().NewTransaction(type, name); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + + tree.RootMessageId = rootMessageId; + tree.ParentMessageId = serverMessageId; + if (!string.IsNullOrEmpty(currentMessageId)) + { + tree.MessageId = currentMessageId; + } + tran.Status = "0"; + + if (response != null) + { + if (!string.IsNullOrWhiteSpace(rootMessageId)) + response.AddHeader(CatHelper.CatRootId, rootMessageId); + if (!string.IsNullOrWhiteSpace(currentMessageId)) + response.AddHeader(CatHelper.CatParentId, currentMessageId); + response.AddHeader(CatHelper.CatId, tree.MessageId); + } + + Cat.GetProducer().LogEvent(type, type + ".Server", "0", getURLServerValue(request)); + Cat.GetProducer().LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); + Cat.GetProducer().LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); + return tran; } - tran.Status = "0"; - - if (response != null) + catch { - if (!string.IsNullOrWhiteSpace(rootMessageId)) - response.AddHeader(CatHelper.CatRootId, rootMessageId); - if (!string.IsNullOrWhiteSpace(currentMessageId)) - response.AddHeader(CatHelper.CatParentId, currentMessageId); - response.AddHeader(CatHelper.CatId, tree.MessageId); + return null; } - - Cat.LogEvent(type, type + ".Server", "0", getURLServerValue(request)); - Cat.LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); - Cat.LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); - - return tran; } public static string GetRootMessageId() From 70d6e3df6b6c9fb4ff6d1b989a45c133190a597f Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 30 Jan 2015 21:17:42 +0800 Subject: [PATCH 18/21] =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E4=B8=B2=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Util/CatHelper.cs | 71 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs index 05a865f..5212c5c 100644 --- a/Util/CatHelper.cs +++ b/Util/CatHelper.cs @@ -12,9 +12,25 @@ namespace Com.Dianping.Cat.Util { public static class CatHelper { - public const string CatRootId = "X-Cat-RootId"; - public const string CatParentId = "X-Cat-ParentId"; - public const string CatId = "X-Cat-Id"; + public class CatRequestMessage + { + public string CatRootId { get; private set; } + public string CatParentId { get; private set; } + public string CatId { get; private set; } + public string RequestMothed { get; private set; } + + public CatRequestMessage(string catRootId, string catParentId, string catId, string requestMothed) + { + CatRootId = catRootId; + CatParentId = catParentId; + CatId = catId; + RequestMothed = requestMothed; + } + } + + private const string CatRootId = "X-Cat-RootId"; + private const string CatParentId = "X-Cat-ParentId"; + private const string CatId = "X-Cat-Id"; public static ITransaction BeginClientTransaction(string type, string name, WebRequest request) { @@ -92,6 +108,55 @@ public static ITransaction BeginServerTransaction(string type, string name = nul } } + public static ITransaction BeginRequestTransaction(out CatRequestMessage catRequestMessage, string type, string name, string requestMothed) + { + var tran = Cat.NewTransaction(type, name); + Cat.LogEvent(type, name, "0", string.Format("Mothed.Request : {0}", requestMothed)); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + string serverMessageId = Cat.GetProducer().CreateMessageId(); + string rootMessageId = (tree.RootMessageId ?? tree.MessageId); + string currentMessageId = tree.MessageId; + Cat.LogEvent("RemoteCall", "Request", "0", serverMessageId); + + catRequestMessage = new CatRequestMessage(rootMessageId, currentMessageId, serverMessageId, name); + + return tran; + } + + public static ITransaction BeginResponseTransaction(CatRequestMessage catRequestMessage, string type, string responseMothed) + { + if (catRequestMessage == null) + throw new ArgumentNullException("catRequestMessage"); + string rootMessageId = catRequestMessage.CatRootId; + string serverMessageId = catRequestMessage.CatParentId; + string currentMessageId = catRequestMessage.CatId; + + var tran = Cat.NewTransaction(type, responseMothed); + var tree = Cat.GetManager().ThreadLocalMessageTree; + if (tree == null) + { + Cat.GetManager().Setup(); + tree = Cat.GetManager().ThreadLocalMessageTree; + } + + tree.RootMessageId = rootMessageId; + tree.ParentMessageId = serverMessageId; + if (!string.IsNullOrEmpty(currentMessageId)) + { + tree.MessageId = currentMessageId; + } + + Cat.LogEvent(type, responseMothed, "0", string.Format("Mothed.Response : {0}", catRequestMessage.RequestMothed)); + + return tran; + } + + public static string GetRootMessageId() { if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) From 8ca915cdec06abda7e72257a0f7a8f4b80234b93 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Thu, 12 Mar 2015 16:38:55 +0800 Subject: [PATCH 19/21] clear code --- Cat.cs | 5 - Message/ITransaction.cs | 134 +++++++++++----------- Util/CatHelper.cs | 229 ------------------------------------- Web/CatHttpAsyncHandler.cs | 88 -------------- Web/CatHttpHandler.cs | 49 -------- Web/CatHttpModule.cs | 41 ------- 6 files changed, 67 insertions(+), 479 deletions(-) delete mode 100644 Util/CatHelper.cs delete mode 100644 Web/CatHttpAsyncHandler.cs delete mode 100644 Web/CatHttpHandler.cs delete mode 100644 Web/CatHttpModule.cs diff --git a/Cat.cs b/Cat.cs index 249308b..28bc714 100644 --- a/Cat.cs +++ b/Cat.cs @@ -113,11 +113,6 @@ public static IEvent NewEvent(string type, string name) return Cat.GetProducer().NewEvent(type, name); } - public static IHeartbeat NewHeartbeat(string type,string name) - { - return Cat.GetProducer().NewHeartbeat(type, name); - } - public static ITransaction NewTransaction(string type, string name) { return Cat.GetProducer().NewTransaction(type, name); diff --git a/Message/ITransaction.cs b/Message/ITransaction.cs index 7f7b212..ac8813c 100644 --- a/Message/ITransaction.cs +++ b/Message/ITransaction.cs @@ -2,92 +2,92 @@ namespace Com.Dianping.Cat.Message { - /** - *

    - * Transaction is any interesting unit of work that takes time to - * complete and may fail. - *

    - * - *

    - * Basically, all data access across the boundary needs to be logged as a - * Transaction since it may fail and time consuming. For example, - * URL request, disk IO, JDBC query, search query, HTTP request, 3rd party API - * call etc. - *

    - * - *

    - * Sometime if A needs call B which is owned by another team, although A and B - * are deployed together without any physical boundary. To make the ownership - * clear, there could be some Transaction logged when A calls B. - *

    - * - *

    - * Most of Transaction should be logged in the infrastructure level - * or framework level, which is transparent to the application. - *

    - * - *

    - * All CAT message will be constructed as a message tree and send to back-end - * for further analysis, and for monitoring. Only Transaction can - * be a tree node, all other message will be the tree leaf. The transaction - * without other messages nested is an atomic transaction. - *

    - * - * @author Frankie Wu + /** + *

    + * Transaction is any interesting unit of work that takes time to + * complete and may fail. + *

    + * + *

    + * Basically, all data access across the boundary needs to be logged as a + * Transaction since it may fail and time consuming. For example, + * URL request, disk IO, JDBC query, search query, HTTP request, 3rd party API + * call etc. + *

    + * + *

    + * Sometime if A needs call B which is owned by another team, although A and B + * are deployed together without any physical boundary. To make the ownership + * clear, there could be some Transaction logged when A calls B. + *

    + * + *

    + * Most of Transaction should be logged in the infrastructure level + * or framework level, which is transparent to the application. + *

    + * + *

    + * All CAT message will be constructed as a message tree and send to back-end + * for further analysis, and for monitoring. Only Transaction can + * be a tree node, all other message will be the tree leaf. The transaction + * without other messages nested is an atomic transaction. + *

    + * + * @author Frankie Wu */ public interface ITransaction : IMessage { - /** - * Get all children message within current transaction. - * - *

    - * Typically, a Transaction can nest other - * Transactions, Events and Heartbeat - * s, while an Event or Heartbeat can't nest other - * messages. - *

    - * - * @return all children messages, empty if there is no nested children. + /** + * Get all children message within current transaction. + * + *

    + * Typically, a Transaction can nest other + * Transactions, Events and Heartbeat + * s, while an Event or Heartbeat can't nest other + * messages. + *

    + * + * @return all children messages, empty if there is no nested children. */ IList Children { get; } - /** - * How long the transaction took from construction to complete. Time unit is - * microsecond. - * - * @return duration time in microsecond + /** + * How long the transaction took from construction to complete. Time unit is + * microsecond. + * + * @return duration time in microsecond */ long DurationInMicros { get; set; } - /** - * How long the transaction took from construction to complete. Time unit is - * millisecond. - * - * @return duration time in millisecond + /** + * How long the transaction took from construction to complete. Time unit is + * millisecond. + * + * @return duration time in millisecond */ long DurationInMillis { get; set; } - /** - * Check if the transaction is stand-alone or belongs to another one. - * - * @return true if it's an root transaction. + /** + * Check if the transaction is stand-alone or belongs to another one. + * + * @return true if it's an root transaction. */ bool Standalone { get; set; } - /** - * Add one nested child message to current transaction. - * - * @param message - * to be added + /** + * Add one nested child message to current transaction. + * + * @param message + * to be added */ ITransaction AddChild(IMessage message); - /** - * Has children or not. An atomic transaction does not have any children - * message. - * - * @return true if child exists, else false. + /** + * Has children or not. An atomic transaction does not have any children + * message. + * + * @return true if child exists, else false. */ bool HasChildren(); } diff --git a/Util/CatHelper.cs b/Util/CatHelper.cs deleted file mode 100644 index 5212c5c..0000000 --- a/Util/CatHelper.cs +++ /dev/null @@ -1,229 +0,0 @@ -using Com.Dianping.Cat.Message; -using Com.Dianping.Cat.Util; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Text; -using System.Web; - -namespace Com.Dianping.Cat.Util -{ - public static class CatHelper - { - public class CatRequestMessage - { - public string CatRootId { get; private set; } - public string CatParentId { get; private set; } - public string CatId { get; private set; } - public string RequestMothed { get; private set; } - - public CatRequestMessage(string catRootId, string catParentId, string catId, string requestMothed) - { - CatRootId = catRootId; - CatParentId = catParentId; - CatId = catId; - RequestMothed = requestMothed; - } - } - - private const string CatRootId = "X-Cat-RootId"; - private const string CatParentId = "X-Cat-ParentId"; - private const string CatId = "X-Cat-Id"; - - public static ITransaction BeginClientTransaction(string type, string name, WebRequest request) - { - var tran = Cat.NewTransaction(type, name); - tran.Status = "0"; - Cat.LogEvent(type, request.RequestUri.AbsolutePath, "0", request.RequestUri.ToString()); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - string serverMessageId = Cat.GetProducer().CreateMessageId(); - string rootMessageId = (tree.RootMessageId ?? tree.MessageId); - string currentMessageId = tree.MessageId; - Cat.LogEvent("RemoteCall", "HttpRequest", "0", serverMessageId); - - request.Headers.Add(CatHelper.CatRootId, rootMessageId); - request.Headers.Add(CatHelper.CatParentId, currentMessageId); - request.Headers.Add(CatHelper.CatId, serverMessageId); - - return tran; - } - - public static ITransaction BeginServerTransaction(string type, string name = null, HttpResponse response = null) - { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - return null; - try - { - var httpContext = System.Web.HttpContext.Current; - if (httpContext == null) { return null; } - var request = httpContext.Request; - - string rootMessageId = request.Headers[CatHelper.CatRootId]; - string serverMessageId = request.Headers[CatHelper.CatParentId]; - string currentMessageId = request.Headers[CatHelper.CatId]; - - if (string.IsNullOrWhiteSpace(name)) - name = request.Path; - - var tran = Cat.GetProducer().NewTransaction(type, name); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - - tree.RootMessageId = rootMessageId; - tree.ParentMessageId = serverMessageId; - if (!string.IsNullOrEmpty(currentMessageId)) - { - tree.MessageId = currentMessageId; - } - tran.Status = "0"; - - if (response != null) - { - if (!string.IsNullOrWhiteSpace(rootMessageId)) - response.AddHeader(CatHelper.CatRootId, rootMessageId); - if (!string.IsNullOrWhiteSpace(currentMessageId)) - response.AddHeader(CatHelper.CatParentId, currentMessageId); - response.AddHeader(CatHelper.CatId, tree.MessageId); - } - - Cat.GetProducer().LogEvent(type, type + ".Server", "0", getURLServerValue(request)); - Cat.GetProducer().LogEvent(type, type + ".Method", "0", getURLMethodValue(request)); - Cat.GetProducer().LogEvent(type, type + ".Client", "0", AppEnv.GetClientIp(request)); - return tran; - } - catch - { - return null; - } - } - - public static ITransaction BeginRequestTransaction(out CatRequestMessage catRequestMessage, string type, string name, string requestMothed) - { - var tran = Cat.NewTransaction(type, name); - Cat.LogEvent(type, name, "0", string.Format("Mothed.Request : {0}", requestMothed)); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - string serverMessageId = Cat.GetProducer().CreateMessageId(); - string rootMessageId = (tree.RootMessageId ?? tree.MessageId); - string currentMessageId = tree.MessageId; - Cat.LogEvent("RemoteCall", "Request", "0", serverMessageId); - - catRequestMessage = new CatRequestMessage(rootMessageId, currentMessageId, serverMessageId, name); - - return tran; - } - - public static ITransaction BeginResponseTransaction(CatRequestMessage catRequestMessage, string type, string responseMothed) - { - if (catRequestMessage == null) - throw new ArgumentNullException("catRequestMessage"); - string rootMessageId = catRequestMessage.CatRootId; - string serverMessageId = catRequestMessage.CatParentId; - string currentMessageId = catRequestMessage.CatId; - - var tran = Cat.NewTransaction(type, responseMothed); - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - - tree.RootMessageId = rootMessageId; - tree.ParentMessageId = serverMessageId; - if (!string.IsNullOrEmpty(currentMessageId)) - { - tree.MessageId = currentMessageId; - } - - Cat.LogEvent(type, responseMothed, "0", string.Format("Mothed.Response : {0}", catRequestMessage.RequestMothed)); - - return tran; - } - - - public static string GetRootMessageId() - { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - { - return string.Empty; - } - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - - return string.IsNullOrEmpty(tree.RootMessageId) ? tree.MessageId : tree.RootMessageId; - } - - public static string GetMessageId() - { - if (!Cat.IsInitialized()) { return string.Empty; } - if (!Cat.GetManager().CatEnabled) { return string.Empty; } - var tree = Cat.GetManager().ThreadLocalMessageTree; - if (tree == null) - { - Cat.GetManager().Setup(); - tree = Cat.GetManager().ThreadLocalMessageTree; - } - return tree.MessageId; - } - - #region url info - private static string getURLServerValue(HttpRequest request) - { - if (request == null) - return string.Empty; - StringBuilder sb = new StringBuilder(); - sb.Append("IPS=").Append(AppEnv.GetClientIp(request)); - sb.Append("&VirtualIP=").Append(AppEnv.GetRemoteIp(request)); - sb.Append("&Server=").Append(AppEnv.IP); - sb.Append("&Referer=").Append(request.getHeader("Referer")); - sb.Append("&Agent=").Append(request.getHeader("User-Agent")); - return sb.ToString(); - } - - private static string getURLMethodValue(HttpRequest request) - { - StringBuilder sb = new StringBuilder(); - sb.Append(request.getServerVariables("Request_Method")).Append(" "); - sb.Append(request.Url.ToString()); - return sb.ToString(); - } - - private static string getHeader(this HttpRequest request, string key) - { - if (request == null) - request = HttpContext.Current.Request; - var headerKey = request.Headers.AllKeys.Where(p => p.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - return request.Headers[headerKey] ?? "null"; - } - - private static string getServerVariables(this HttpRequest request, string key) - { - if (request == null) - request = HttpContext.Current.Request; - var headerKey = request.ServerVariables.AllKeys.Where(p => p.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - return request.ServerVariables[headerKey] ?? "null"; - } - #endregion - - } -} diff --git a/Web/CatHttpAsyncHandler.cs b/Web/CatHttpAsyncHandler.cs deleted file mode 100644 index 68db53d..0000000 --- a/Web/CatHttpAsyncHandler.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Web.SessionState; -using Com.Dianping.Cat.Util; -using System.Threading; -using Com.Dianping.Cat.Message; - -namespace Com.Dianping.Cat.Web -{ - public class CatHttpAsyncHandler : IHttpAsyncHandler, IRequiresSessionState - { - private IHttpAsyncHandler asyncHandler; - - public bool IsReusable { get { return asyncHandler.IsReusable; } } - - public CatHttpAsyncHandler(IHttpAsyncHandler asyncHandler) - { - this.asyncHandler = asyncHandler; - } - - public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) - { - var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); - try - { - if (extraData == null) - extraData = tran; - - return asyncHandler.BeginProcessRequest(context, cb, extraData); - } - catch (Exception ex) - { - Cat.GetProducer().LogError(ex); - tran.SetStatus(ex); - throw; - } - } - - public void EndProcessRequest(IAsyncResult result) - { - ITransaction tran = null; - try - { - var extraData = result.AsyncState as ITransaction; - if (extraData != null) - tran = extraData; - - asyncHandler.EndProcessRequest(result); - } - catch (Exception ex) - { - Cat.GetProducer().LogError(ex); - tran.SetStatus(ex); - throw; - } - finally - { - tran.Complete(); - } - } - - public void ProcessRequest(HttpContext context) - { - var tran = CatHelper.BeginServerTransaction("URL", response: context.Response); - try - { - asyncHandler.ProcessRequest(context); - } - catch (Exception ex) - { - if (ex.GetBaseException() is ThreadAbortException) - { - return; - } - Cat.GetProducer().LogError(ex); - tran.SetStatus(ex); - throw; - } - finally - { - tran.Complete(); - } - } - } -} diff --git a/Web/CatHttpHandler.cs b/Web/CatHttpHandler.cs deleted file mode 100644 index 3f973ab..0000000 --- a/Web/CatHttpHandler.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Web.SessionState; -using Com.Dianping.Cat.Util; -using System.Threading; -using Com.Dianping.Cat.Message; - -namespace Com.Dianping.Cat.Web -{ - public class CatHttpHandler : IHttpHandler, IRequiresSessionState - { - private ITransaction tran = null; - private IHttpHandler handler; - - public bool IsReusable { get { return handler.IsReusable; } } - - public CatHttpHandler(IHttpHandler httpHandler) - { - this.handler = httpHandler; - } - - public void ProcessRequest(HttpContext context) - { - tran = CatHelper.BeginServerTransaction("URL", response: context.Response); - try - { - handler.ProcessRequest(context); - } - catch (Exception ex) - { - var baseEx = ex.GetBaseException(); - if (baseEx is ThreadAbortException) - { - return; - } - Cat.GetProducer().LogError(ex); - tran.SetStatus(ex); - throw; - } - finally - { - tran.Complete(); - } - } - } -} diff --git a/Web/CatHttpModule.cs b/Web/CatHttpModule.cs deleted file mode 100644 index ced1892..0000000 --- a/Web/CatHttpModule.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using Com.Dianping.Cat.Message; -using Com.Dianping.Cat.Util; -using System.IO; - -namespace Com.Dianping.Cat.Web -{ - public class CatHttpModule : IHttpModule - { - public void Init(HttpApplication context) - { - context.PostMapRequestHandler += context_PostMapRequestHandler; - } - - void context_PostMapRequestHandler(object sender, EventArgs e) - { - if (!Cat.IsInitialized() || !Cat.GetManager().CatEnabled) - { - return; - } - - var context = System.Web.HttpContext.Current; - - if (context == null || context.Handler == null) - return; - - if (context.Handler is IHttpAsyncHandler) - context.Handler = new CatHttpAsyncHandler((IHttpAsyncHandler)context.Handler); - else - context.Handler = new CatHttpHandler(context.Handler); - } - - public void Dispose() - { - } - } -} From 1902a19823fbc6eb74ab910bc6a9e7ef6c364820 Mon Sep 17 00:00:00 2001 From: chinaboard Date: Thu, 12 Mar 2015 17:08:56 +0800 Subject: [PATCH 20/21] fix csproj --- Cat.Net.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cat.Net.csproj b/Cat.Net.csproj index 8e9e5ce..8d038f9 100644 --- a/Cat.Net.csproj +++ b/Cat.Net.csproj @@ -54,7 +54,6 @@ - @@ -95,9 +94,6 @@ - - - From c15184de709860966ed37c034d6d9cd1cdd00fda Mon Sep 17 00:00:00 2001 From: chinaboard Date: Fri, 20 Mar 2015 09:18:43 +0800 Subject: [PATCH 21/21] format code,roll back AddData --- Message/IMessage.cs | 132 +++++++++++++-------------- Message/Internals/AbstractMessage.cs | 2 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/Message/IMessage.cs b/Message/IMessage.cs index eec8ff6..cb06003 100644 --- a/Message/IMessage.cs +++ b/Message/IMessage.cs @@ -2,106 +2,106 @@ namespace Com.Dianping.Cat.Message { - /** - *

    - * Message represents data collected during application runtime. It will be sent - * to back-end system asynchronous for further processing. - *

    - * - *

    - * Super interface of Event, Heartbeat and - * Transaction. - *

    - * - * @see Event, Heartbeat, Transaction - * @author Frankie Wu + /** + *

    + * Message represents data collected during application runtime. It will be sent + * to back-end system asynchronous for further processing. + *

    + * + *

    + * Super interface of Event, Heartbeat and + * Transaction. + *

    + * + * @see Event, Heartbeat, Transaction + * @author Frankie Wu */ public interface IMessage { - /** - * @return key value pairs data + /** + * @return key value pairs data */ string Data { get; } - /** - * Message name. - * - * @return message name + /** + * Message name. + * + * @return message name */ string Name { get; } - /** - * Get the message status. - * - * @return message status. "0" means success, otherwise error code. + /** + * Get the message status. + * + * @return message status. "0" means success, otherwise error code. */ string Status { get; set; } - /** - * The time stamp the message was created. - * - * @return message creation time stamp in milliseconds + /** + * The time stamp the message was created. + * + * @return message creation time stamp in milliseconds */ long Timestamp { get; set; } - /** - * Message type. - * - *

    - * Typical message types are: - *

      - *
    • URL: maps to one method of an action
    • - *
    • Service: maps to one method of service call
    • - *
    • Search: maps to one method of search call
    • - *
    • SQL: maps to one SQL statement
    • - *
    • Cache: maps to one cache access
    • - *
    • Error: maps to java.lang.Throwable (java.lang.Exception and java.lang.Error)
    • - *
    - *

    - * - * @return message type + /** + * Message type. + * + *

    + * Typical message types are: + *

      + *
    • URL: maps to one method of an action
    • + *
    • Service: maps to one method of service call
    • + *
    • Search: maps to one method of search call
    • + *
    • SQL: maps to one SQL statement
    • + *
    • Cache: maps to one cache access
    • + *
    • Error: maps to java.lang.Throwable (java.lang.Exception and java.lang.Error)
    • + *
    + *

    + * + * @return message type */ string Type { get; } - /** - * add one or multiple key-value pairs to the message. - * - * @param keyValuePairs - * key-value pairs like 'a=1&b=2&...' + /** + * add one or multiple key-value pairs to the message. + * + * @param keyValuePairs + * key-value pairs like 'a=1&b=2&...' */ void AddData(String keyValuePairs); - /** - * add one key-value pair to the message. - * - * @param key - * @param value + /** + * add one key-value pair to the message. + * + * @param key + * @param value */ void AddData(String key, Object value); - /** - * Complete the message construction. + /** + * Complete the message construction. */ void Complete(); - /** - * If the complete() method was called or not. - * - * @return true means the complete() method was called, false otherwise. + /** + * If the complete() method was called or not. + * + * @return true means the complete() method was called, false otherwise. */ bool IsCompleted(); - /** - * @return + /** + * @return */ bool IsSuccess(); - /** - * Set the message status with exception class name. - * - * @param e - * exception. + /** + * Set the message status with exception class name. + * + * @param e + * exception. */ void SetStatus(Exception e); } diff --git a/Message/Internals/AbstractMessage.cs b/Message/Internals/AbstractMessage.cs index 2b0ad48..ba15fa2 100644 --- a/Message/Internals/AbstractMessage.cs +++ b/Message/Internals/AbstractMessage.cs @@ -68,7 +68,7 @@ public void AddData(String keyValuePairs) } else { - _mData.Append('&').Append(keyValuePairs); + _mData.Append(keyValuePairs); } }