/// <summary> /// Generate the JavaScript to create a logger. /// </summary> /// <param name="loggerVariableName"> /// New logger object will be assigned to this JS variable. /// </param> /// <param name="loggerName"> /// Name of the logger. Could be null (for the root logger). /// </param> /// <param name="sb"> /// JS code will be appended to this. /// </param> public static void GenerateLogger(string loggerVariableName, string loggerName, StringBuilder sb) { string quotedLoggerName = loggerName == null ? "" : @"""" + loggerName + @""""; JavaScriptHelpers.WriteLine(string.Format("var {0}=JL({1});", loggerVariableName, quotedLoggerName), sb); }
/// <summary> /// Generates the JavaScript for the call to setOptions to set the options for an object /// (the JL object itself, an appender or a logger). /// </summary> /// <param name="parentName"> /// JavaScript variable that holds the element. /// </param> /// <param name="xe"> /// XML element. The attributes on this element will provide the values for the options. /// </param> /// <param name="attributeInfos"> /// Describes which attributes to use as options and how to validate them. /// /// As regards attributeInfos that have a SubTagName: /// * The value of such an attribute is an array, for example [ 'a', 'b' ] /// * If there are no child elements with the given sub tag name, there is no value, and no entry for that attributeinfo in /// the generated setOption. /// * If there is only one child element and it does not have an attribute, the value is an empty array []. /// </param> /// <param name="initialAttributeValues"> /// Initial attribute values. The elements found in xe will be added to this. /// If null, this method will create an empty collection itself. /// </param> /// <param name="sb"> /// The JS code is added to this. /// </param> /// <param name="validate"> /// If not null, this method is called on the generated attribute values. This can be used to throw an exception /// if the given parameters are not valid. /// </param> public static void ProcessOptionAttributes(string parentName, XmlElement xe, IEnumerable <AttributeInfo> attributeInfos, AttributeValueCollection initialAttributeValues, StringBuilder sb, Action <AttributeValueCollection> validate = null) { var attributeValues = initialAttributeValues ?? new AttributeValueCollection(); XmlHelpers.ProcessAttributes(xe, attributeInfos, attributeValues); if (validate != null) { validate(attributeValues); } JavaScriptHelpers.GenerateSetOptions(parentName, 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 ({1}) {{", Constants.GlobalMethodCalledAfterJsnlogJsLoaded, Constants.JsLogObjectName), 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}({1}); }} catch(e) {{}};", Constants.GlobalMethodCalledAfterJsnlogJsLoaded, Constants.JsLogObjectName), 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); } }
/// <summary> /// Generates the JavaScript create an object. /// </summary> /// <param name="objectVariableName"></param> /// <param name="createMethodName"></param> /// <param name="name"> /// Name of the object as known to the user. For example the appender name. /// </param> /// <param name="sb"></param> public static void GenerateCreate(string objectVariableName, string createMethodName, string name, StringBuilder sb) { JavaScriptHelpers.WriteLine(string.Format("var {0}=JL.{1}('{2}');", objectVariableName, createMethodName, name), 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(StringBuilder sb, Func <string, string> virtualToAbsoluteFunc, string userIp, string requestId, bool generateClosure) { Dictionary <string, string> appenderNames = new Dictionary <string, string>(); JsnlogConfiguration jsnlogConfiguration = JavascriptLogging.GetJsnlogConfiguration(); string loggerProductionLibraryVirtualPath = jsnlogConfiguration.productionLibraryPath; bool loggerEnabled = jsnlogConfiguration.enabled; string loggerProductionLibraryPath = null; if (!string.IsNullOrEmpty(loggerProductionLibraryVirtualPath)) { // Every hard coded path must be resolved. See the declaration of DefaultDefaultAjaxUrl loggerProductionLibraryPath = Utils.AbsoluteUrl(loggerProductionLibraryVirtualPath, virtualToAbsoluteFunc); } JavaScriptHelpers.WriteJavaScriptBeginTag(sb); if (generateClosure) { JavaScriptHelpers.WriteLine(string.Format("var {0} = function ({1}) {{", Constants.GlobalMethodCalledAfterJsnlogJsLoaded, Constants.JsLogObjectName), sb); } // Generate setOptions for JSNLog object itself var jsonFields = new List <string>(); JavaScriptHelpers.AddJsonField(jsonFields, Constants.JsLogObjectClientIpOption, userIp, new StringValue()); JavaScriptHelpers.AddJsonField(jsonFields, Constants.JsLogObjectRequestIdOption, requestId, new StringValue()); JavaScriptHelpers.GenerateSetOptions(Constants.JsLogObjectName, jsnlogConfiguration, appenderNames, virtualToAbsoluteFunc, sb, jsonFields); if (loggerEnabled) { // Process all loggers and appenders. First process the appenders, because the loggers can be // dependent on the appenders, and will use appenderNames to translate configuration appender names // to JavaScript names. int sequence = 0; GenerateCreateJavaScript(jsnlogConfiguration.ajaxAppenders, sb, virtualToAbsoluteFunc, appenderNames, ref sequence); GenerateCreateJavaScript(jsnlogConfiguration.consoleAppenders, sb, virtualToAbsoluteFunc, appenderNames, ref sequence); GenerateCreateJavaScript(jsnlogConfiguration.loggers, sb, virtualToAbsoluteFunc, appenderNames, ref sequence); } // ------------- 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}({1}); }} catch(e) {{}};", Constants.GlobalMethodCalledAfterJsnlogJsLoaded, Constants.JsLogObjectName), 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); } }