// -------------------------------- public void ProcessDummyAppender(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, IEnumerable<AttributeInfo> appenderAttributes, StringBuilder sb) { // Ensure no child xml elements have been used with the appender element // by passing in an empty list. var childTagInfos = new List<XmlHelpers.TagInfo>(); ProcessAppender(xe, parentName, appenderNames, sequence, appenderAttributes, "createDummyAppender", childTagInfos, sb); }
protected void ProcessAppender(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, IEnumerable<AttributeInfo> appenderAttributes, string jsCreateMethodName, List<XmlHelpers.TagInfo> childTagInfos, StringBuilder sb) { if (xe == null) { return; } string appenderName = XmlHelpers.RequiredAttribute(xe, "name"); string appenderVariableName = string.Format("{0}{1}", Constants.JsAppenderVariablePrefix, sequence.Next()); appenderNames[appenderName] = appenderVariableName; JavaScriptHelpers.GenerateCreate(appenderVariableName, jsCreateMethodName, appenderName, sb); Utils.ProcessOptionAttributes( appenderVariableName, xe, appenderAttributes, null, sb, (attributeValues) => { Validate(appenderName, attributeValues); }); XmlHelpers.ProcessNodeList( xe.ChildNodes, childTagInfos, appenderVariableName, appenderNames, sequence, sb); }
// -------------------------------- public void ProcessLogger(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, IEnumerable<AttributeInfo> loggerAttributes, StringBuilder sb) { if (xe == null) { return; } var appendersValue = new AppendersValue(appenderNames); string appenders = XmlHelpers.OptionalAttribute(xe, "appenders", null, appendersValue.ValidValueRegex); string loggerName = XmlHelpers.OptionalAttribute(xe, "name", ""); JavaScriptHelpers.GenerateLogger(Constants.JsLoggerVariable, loggerName, sb); AttributeValueCollection attributeValues = new AttributeValueCollection(); if (appenders != null) { attributeValues[Constants.JsLoggerAppendersOption] = new Value(appenders, appendersValue); } Utils.ProcessOptionAttributes(Constants.JsLoggerVariable, xe, loggerAttributes, attributeValues, sb); }
// This version is not reliant on sitting in a web site, so can be unit tested. // generateClosure - if false, no function closure is generated around the generated JS code. Only set to false when unit testing. // Doing this assumes that jsnlog.js has loaded before the code generated by the method is executed. // // You want to set this to false during unit testing, because then you need direct access outside the closure of variables // that are private to the closure, specifically dummyappenders that store the log messages you receive, so you can unit test them. public void ProcessRootExec(XmlElement xe, StringBuilder sb, Func<string, string> virtualToAbsoluteFunc, string userIp, string requestId, bool generateClosure) { string loggerProductionLibraryVirtualPath = XmlHelpers.OptionalAttribute(xe, "productionLibraryPath", ""); bool loggerEnabled = bool.Parse(XmlHelpers.OptionalAttribute(xe, "enabled", "true", Constants.RegexBool)); string loggerProductionLibraryPath = null; if (!string.IsNullOrEmpty(loggerProductionLibraryVirtualPath)) { // Every hard coded path must be resolved. See the declaration of DefaultDefaultAjaxUrl loggerProductionLibraryPath = virtualToAbsoluteFunc(loggerProductionLibraryVirtualPath); } if (!loggerEnabled) { if (!string.IsNullOrWhiteSpace(loggerProductionLibraryPath)) { JavaScriptHelpers.WriteScriptTag(loggerProductionLibraryPath, sb); } JavaScriptHelpers.WriteJavaScriptBeginTag(sb); Utils.ProcessOptionAttributes(Constants.JsLogObjectName, xe, Constants.JSNLogAttributes, null, sb); JavaScriptHelpers.WriteJavaScriptEndTag(sb); return; } JavaScriptHelpers.WriteJavaScriptBeginTag(sb); if (generateClosure) { JavaScriptHelpers.WriteLine(string.Format("var {0} = function () {{", Constants.GlobalMethodCalledAfterJsnlogJsLoaded), sb); } // Generate setOptions for JSNLog object itself AttributeValueCollection attributeValues = new AttributeValueCollection(); attributeValues[Constants.JsLogObjectClientIpOption] = new Value(userIp, new StringValue()); attributeValues[Constants.JsLogObjectRequestIdOption] = new Value(requestId, new StringValue()); // Set default value for defaultAjaxUrl attribute attributeValues[Constants.JsLogObjectDefaultAjaxUrlOption] = new Value(virtualToAbsoluteFunc(Constants.DefaultDefaultAjaxUrl), new StringValue()); Utils.ProcessOptionAttributes(Constants.JsLogObjectName, xe, Constants.JSNLogAttributes, attributeValues, sb); // Process all loggers and appenders Dictionary<string, string> appenderNames = new Dictionary<string, string>(); Sequence sequence = new Sequence(); // ----------------- // First process all assembly tags topLeveltagInfos = new List<XmlHelpers.TagInfo>( new[] { new XmlHelpers.TagInfo(Constants.TagAssembly, ProcessAssembly, Constants.AssemblyAttributes, (int)Constants.OrderNbr.Assembly) }); XmlHelpers.ProcessNodeList( xe.ChildNodes, topLeveltagInfos.Where(t => t.Tag == Constants.TagAssembly).ToList(), null, appenderNames, sequence, sb, string.Format("^{0}*", Constants.TagAssembly)); // ----------------- // The elements (if any) from external assemblies have now been loaded (with the assembly elements). // Now add the elements from the executing assembly after the external ones. // This way, logger elements are processed last - after any new appenders. AddAssemblyTagInfos(Assembly.GetExecutingAssembly()); // Now process the external and internal elements, but not the assembly elements. XmlHelpers.ProcessNodeList( xe.ChildNodes, topLeveltagInfos, null, appenderNames, sequence, sb, string.Format("^((?!{0}).)*$", Constants.TagAssembly)); // ------------- if (generateClosure) { // Generate code to execute the function, in case jsnlog.js has already been loaded. // Wrap in try catch, so if jsnlog.js hasn't been loaded, the resulting exception will be swallowed. JavaScriptHelpers.WriteLine(string.Format("}}; try {{ {0}(); }} catch(e) {{}};", Constants.GlobalMethodCalledAfterJsnlogJsLoaded), sb); } JavaScriptHelpers.WriteJavaScriptEndTag(sb); // Write the script tag that loads jsnlog.js after the code generated from the web.config. // When using jsnlog.js as an AMD module or in a bundle, jsnlog.js will be loaded after that code as well, // and creating a similar situation in the default out of the box loading option makes it more likely // you pick up bugs during testing. if (!string.IsNullOrWhiteSpace(loggerProductionLibraryPath)) { JavaScriptHelpers.WriteScriptTag(loggerProductionLibraryPath, sb); } }
private void ProcessAssembly(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, IEnumerable<AttributeInfo> assemblyAttributes, StringBuilder sb) { if (xe == null) { return; } string assemblyName = XmlHelpers.RequiredAttribute(xe, "name"); AddAssemblyTagInfos(Assembly.Load(assemblyName)); }
/// <summary> /// Processes a list of nodes. All XmlElements among the nodes are processed, /// any other types of nodes (comments, etc.) are ignored. /// </summary> /// <param name="xmlNodeList"> /// The list of nodes /// </param> /// <param name="tagInfos"> /// Specifies all tags (= node names) that are permissable. You get an exception if there is a node /// that is not listed here. /// /// The nodes are not listed in the order in which they appear in the list. Instead, first all nodes /// with the name listed in the first TagInfo in tagInfos are processed. Then those in the second TagInfo, etc. /// /// This way, you can for example first process all appenders, and then all loggers. /// /// If there are no nodes at all for a tag name given in tagInfo, the /// XmlElementProcessor given in the tagInfo is called once with a null XmlElement. /// </param> /// <param name="context">parentName, appenderNames, sequence and sb get passed to the processor method that is listed for each tag in tagInfos.</param> /// <param name="sequence"></param> /// <param name="sb"></param> public static void ProcessNodeList( XmlNodeList xmlNodeList, IEnumerable<TagInfo> tagInfos, string parentName, Dictionary<string,string> appenderNames, Sequence sequence, StringBuilder sb) { // Ensure there are no child nodes with names that are unknown - that is, not listed in tagInfos foreach (XmlNode xmlNode in xmlNodeList) { XmlElement xe = xmlNode as XmlElement; if (xe != null) { if (!(tagInfos.Any(t => t.Tag == xe.Name))) { throw new UnknownTagException(xe.Name); } } } // Process each child node foreach (TagInfo tagInfo in tagInfos) { int nbrTagsFound = 0; foreach (XmlNode xmlNode in xmlNodeList) { XmlElement xe = xmlNode as XmlElement; if (xe != null) { if (xe.Name == tagInfo.Tag) { // Check that all attributes are valid foreach (XmlAttribute xa in xe.Attributes) { if ((tagInfo.ValidAttributeName == null) || (!tagInfo.ValidAttributeName.IsMatch(xa.Name))) { throw new UnknownAttributeException(xe.Name, xa.Name); } } nbrTagsFound++; tagInfo.XmlElementProcessor(xe, parentName, appenderNames, sequence, sb); } } } if (nbrTagsFound > tagInfo.MaxNbrTags) { throw new TooManyTagsException(tagInfo.Tag, tagInfo.MaxNbrTags, nbrTagsFound); } if (nbrTagsFound == 0) { tagInfo.XmlElementProcessor(null, parentName, appenderNames, sequence, sb); } } }
/// <summary> /// Processes a list of nodes. All XmlElements among the nodes are processed, /// any other types of nodes (comments, etc.) are ignored. /// </summary> /// <param name="xmlNodeList"> /// The list of nodes /// </param> /// <param name="tagInfos"> /// Specifies all tags (= node names) that are permissable. You get an exception if there is a node /// that is not listed here. However, see childNodeNameRegex. /// /// The nodes are not listed in the order in which they appear in the list. Instead, first all nodes /// with the name listed in the first TagInfo in tagInfos are processed. Then those in the second TagInfo, etc. /// /// This way, you can for example first process all appenders, and then all loggers. /// /// If there are no nodes at all for a tag name given in tagInfo, the /// XmlElementProcessor given in the tagInfo is called once with a null XmlElement. /// </param> /// <param name="context">parentName, appenderNames, sequence and sb get passed to the processor method that is listed for each tag in tagInfos.</param> /// <param name="sequence"></param> /// <param name="sb"></param> /// <param name="childNodeNameRegex"> /// If this is given, then only those nodes in xmlNodeList whose name matches childNodeNameRegex will be processed. /// The other nodes will be completely ignored. /// /// If this is not given, no filtering takes place and all nodes are processed. /// </param> public static void ProcessNodeList( XmlNodeList xmlNodeList, List<TagInfo> tagInfos, string parentName, Dictionary<string,string> appenderNames, Sequence sequence, StringBuilder sb, string childNodeNameRegex = ".*") { // Ensure there are no child nodes with names that are unknown - that is, not listed in tagInfos foreach (XmlNode xmlNode in xmlNodeList) { XmlElement xe = xmlNode as XmlElement; if (xe != null) { if (!Regex.IsMatch(xe.Name, childNodeNameRegex)) { continue; } if (!(tagInfos.Any(t => t.Tag == xe.Name))) { throw new UnknownTagException(xe.Name); } } } // Process each child node // // Note that the tagInfo list may be added to when tagInfo.XmlElementProcessor is called. // Because of this, use for, not foreach for (int i = 0; i < tagInfos.Count; i++ ) { TagInfo tagInfo = tagInfos[i]; int nbrTagsFound = 0; foreach (XmlNode xmlNode in xmlNodeList) { XmlElement xe = xmlNode as XmlElement; // If xmlNode is not an XmlElement, ignore it. // This will be the case when xmlNode is a comment. if (xe == null) { continue; } if (!Regex.IsMatch(xe.Name, childNodeNameRegex)) { continue; } if (xe.Name == tagInfo.Tag) { // Check that all attributes are valid EnsureAllAttributesKnown(xe, tagInfo.AttributeInfos); nbrTagsFound++; tagInfo.XmlElementProcessor(xe, parentName, appenderNames, sequence, tagInfo.AttributeInfos, sb); } } if (nbrTagsFound > tagInfo.MaxNbrTags) { throw new TooManyTagsException(tagInfo.Tag, tagInfo.MaxNbrTags, nbrTagsFound); } if (nbrTagsFound == 0) { tagInfo.XmlElementProcessor(null, parentName, appenderNames, sequence, tagInfo.AttributeInfos, sb); } } }
private void ProcessTimerInterval(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { JavaScriptHelpers.ProcessTagWithValue( xe, parentName, Constants.RegexIntegerGreaterZero, "{0}.setTimerInterval({1}); {0}.setTimed(true);", sb); }
/// <summary> /// Processes a configuration (such as the contents of the jsnlog element in web.config). /// /// The configuration is processed into JavaScript, which configures the jsnlog client side library. /// </summary> /// <param name="xe"> /// XmlElement to be processed /// </param> /// <param name="sb"> /// All JavaScript needs to be written to this string builder. /// </param> public void ProcessRoot(XmlElement xe, StringBuilder sb) { string loggerProductionLibraryVirtualPath = XmlHelpers.OptionalAttribute(xe, "productionLibraryPath", ""); string loggerStubLibraryVirtualPath = XmlHelpers.OptionalAttribute(xe, "stubPath", ""); bool loggerEnabled = bool.Parse(XmlHelpers.OptionalAttribute(xe, "enabled", "true", Constants.RegexBool)); string loggerProductionLibraryPath = null; if (!string.IsNullOrEmpty(loggerProductionLibraryVirtualPath)) { loggerProductionLibraryPath = VirtualPathUtility.ToAbsolute(loggerProductionLibraryVirtualPath); } string loggerStubLibraryPath = null; if (!string.IsNullOrEmpty(loggerStubLibraryVirtualPath)) { loggerStubLibraryPath = VirtualPathUtility.ToAbsolute(loggerStubLibraryVirtualPath); } if (!loggerEnabled) { if (!string.IsNullOrWhiteSpace(loggerStubLibraryPath)) { JavaScriptHelpers.WriteScriptTag(loggerStubLibraryPath, sb); } else if (!string.IsNullOrWhiteSpace(loggerProductionLibraryPath)) { JavaScriptHelpers.WriteScriptTag(loggerProductionLibraryPath, sb); } JavaScriptHelpers.WriteJavaScriptBeginTag(sb); JavaScriptHelpers.WriteLine("var jsnlog_disabled = true;", sb); JavaScriptHelpers.WriteJavaScriptEndTag(sb); return; } if (!string.IsNullOrWhiteSpace(loggerProductionLibraryPath)) { JavaScriptHelpers.WriteScriptTag(loggerProductionLibraryPath, sb); } JavaScriptHelpers.WriteJavaScriptBeginTag(sb); JavaScriptHelpers.WriteLine("(function () {", sb); Dictionary<string, string> appenderNames = new Dictionary<string, string>(); Sequence sequence = new Sequence(); XmlHelpers.ProcessNodeList( xe.ChildNodes, new[] { new XmlHelpers.TagInfo("appender", ProcessAppender, new Regex("^(name|url)$")), new XmlHelpers.TagInfo("root", ProcessRoot, null, 1), new XmlHelpers.TagInfo("logger", ProcessLogger, new Regex("^(name|additivity)$")) }, null, appenderNames, sequence, sb); JavaScriptHelpers.WriteLine("}());", sb); JavaScriptHelpers.WriteJavaScriptEndTag(sb); }
private void ProcessRoot(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { JavaScriptHelpers.WriteLine(string.Format("var {0}=jsnlog.getRootLogger();", JsRootLoggerVariable), sb); if (xe == null) { // No root has been defined in the web.config. // But we must have a root with an AjaxAppender. // So create one with a default appender. AddDefaultAppenderToLogger(JsRootLoggerVariable, sb); return; } ProcessLoggerChildren(xe.ChildNodes, JsRootLoggerVariable, appenderNames, sb); }
// Normally used by version. But this method will override version. private void ProcessSessionId(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { JavaScriptHelpers.ProcessTagWithValue( xe, parentName, null, "{0}.setSessionId(\"{1}\");", sb); }
private void ProcessLoggerUserAgentRegex(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { JavaScriptHelpers.ProcessTagWithValue( xe, parentName, null, "{0}.setUserAgentRegex('{1}');", sb); }
private void ProcessLoggerLevel(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { JavaScriptHelpers.ProcessTagWithValue( xe, parentName, Constants.RegexLevels, "{0}.setLevel(jsnlog.Level.{1});", sb); }
private void ProcessLogger(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { if (xe == null) { return; } string loggerName = XmlHelpers.RequiredAttribute(xe, "name"); string additivityEnabled = XmlHelpers.OptionalAttribute(xe, "additivity", AdditivityDefaultValue, Constants.RegexBool); JavaScriptHelpers.WriteLine(string.Format("{0}=jsnlog.getLogger('{1}');", JsLoggerVariable, loggerName), sb); if (additivityEnabled != AdditivityDefaultValue) { JavaScriptHelpers.WriteLine(string.Format("{0}.setAdditivity({1});", JsLoggerVariable, additivityEnabled), sb); } ProcessLoggerChildren(xe.ChildNodes, JsLoggerVariable, appenderNames, sb); }
private void ProcessAppenderRef(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { if (xe == null) { if (parentName == JsRootLoggerVariable) { // A root is being defined without an appender-ref. // So attach a default appender. AddDefaultAppenderToLogger(parentName, sb); } return; } string appenderRef = XmlHelpers.RequiredAttribute(xe, "ref"); if (!appenderNames.ContainsKey(appenderRef)) { throw new UnknownAppenderException(appenderRef); } string appenderVariableName = appenderNames[appenderRef]; JavaScriptHelpers.AddAppenderToLogger(parentName, appenderVariableName, sb); }
// For the interface descriptions of these methods, see XmlHelpers.XmlElementProcessor delegate. // ---- Appender and its children private void ProcessAppender(XmlElement xe, string parentName, Dictionary<string, string> appenderNames, Sequence sequence, StringBuilder sb) { if (xe == null) { return; } string appenderName = XmlHelpers.RequiredAttribute(xe, "name"); string appenderUrl = XmlHelpers.OptionalAttribute( xe, "url", AppenderDefaultUrl, @"^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\-._~:/?#[\]@!$&'()*+,;=]+$"); string appenderVariableName = string.Format("{0}{1}", JsAppenderVariablePrefix, sequence.Next()); appenderNames[appenderName] = appenderVariableName; JavaScriptHelpers.GenerateAppender(appenderVariableName, appenderUrl, sb); XmlHelpers.ProcessNodeList( xe.ChildNodes, new[] { new XmlHelpers.TagInfo("level", ProcessAppenderLevel, new Regex("^value$"), 1), new XmlHelpers.TagInfo("userAgentRegex", ProcessAppenderUserAgentRegex, new Regex("^value$"), 1), new XmlHelpers.TagInfo("ipRegex", ProcessAppenderIpRegex, new Regex("^value$"), 1), new XmlHelpers.TagInfo("batchSize", ProcessBatchSize, new Regex("^value$"), 1), new XmlHelpers.TagInfo("timerInterval", ProcessTimerInterval, new Regex("^value$"), 1), new XmlHelpers.TagInfo("sessionId", ProcessSessionId, new Regex("^value$"), 1) }, appenderVariableName, appenderNames, sequence, sb); }