Simplified parser(s)

This commit is contained in:
Eric Fontana
2014-07-23 08:05:22 -04:00
parent 508ea875ab
commit ecf75282cd
10 changed files with 273 additions and 524 deletions

View File

@@ -17,10 +17,11 @@
<!--Single Tag-->
<Match field="Text" value="%{SYSLOGLINE}" />
</Grok>
<Mutate>
<!-- <Mutate>
<Rename oldName="timestamp" newName="xtimestamp"/>
<Remove field="foo%{Index}" />
</Mutate>
-->
<Date field="timestamp" target="@timestamp" convertToUTC="true">
<Pattern>MMM d HH:mm:ss</Pattern>
<Pattern>MMM dd HH:mm:ss</Pattern>

View File

@@ -260,7 +260,7 @@ namespace TimberWinR.UnitTests
Assert.AreEqual(iCodepage, log.ICodepage);
Assert.AreEqual(recurse, log.Recurse);
Assert.AreEqual(splitLongLines, log.SplitLongLines);
Assert.IsNull(log.ICheckpoint);
name = "Second Set";
location = @"C:\Logs2\*.log";
@@ -275,7 +275,6 @@ namespace TimberWinR.UnitTests
Assert.AreEqual(iCodepage, log.ICodepage);
Assert.AreEqual(recurse, log.Recurse);
Assert.AreEqual(splitLongLines, log.SplitLongLines);
Assert.IsNull(log.ICheckpoint);
name = "Third Set";
@@ -291,7 +290,6 @@ namespace TimberWinR.UnitTests
Assert.AreEqual(iCodepage, log.ICodepage);
Assert.AreEqual(recurse, log.Recurse);
Assert.AreEqual(splitLongLines, log.SplitLongLines);
Assert.IsNull(log.ICheckpoint);
}
[Test]

View File

@@ -58,8 +58,15 @@ namespace TimberWinR
{
validateWithSchema(xmlConfFile, Properties.Resources.configSchema);
parseConfInput(xmlConfFile);
parseConfFilter(xmlConfFile);
try
{
parseConfInput(xmlConfFile);
parseConfFilter(xmlConfFile);
}
catch(Exception ex)
{
LogManager.GetCurrentClassLogger().Error(ex);
}
}
private static void validateWithSchema(string xmlConfFile, string xsdSchema)
@@ -69,8 +76,7 @@ namespace TimberWinR
// Ensure that the xml configuration file provided obeys the xsd schema.
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", XmlReader.Create(new StringReader(xsdSchema)));
#if false
#if true
bool errorsFound = false;
config.Validate(schemas, (o, e) =>
{
@@ -115,55 +121,40 @@ namespace TimberWinR
select el;
string tagName = "Inputs";
if (inputs.Count() == 0)
{
if (inputs.Count() == 0)
throw new TimberWinR.ConfigurationErrors.MissingRequiredTagException(tagName);
}
// WINDOWS EVENTS
IEnumerable<XElement> xml_events =
from el in inputs.Elements("WindowsEvents").Elements("Event")
select el;
foreach (XElement e in xml_events)
{
WindowsEvent.Parse(_events, e);
}
foreach (XElement e in xml_events)
WindowsEvent.Parse(_events, e);
// TEXT LOGS
IEnumerable<XElement> xml_logs =
from el in inputs.Elements("Logs").Elements("Log")
select el;
foreach (XElement e in xml_logs)
{
TailFileInput.Parse(_logs, e);
}
foreach (XElement e in xml_logs)
TailFileInput.Parse(_logs, e);
// IIS LOGS
IEnumerable<XElement> xml_iis =
from el in inputs.Elements("IISLogs").Elements("IISLog")
select el;
foreach (XElement e in xml_iis)
{
IISLog.Parse(_iislogs, e);
}
foreach (XElement e in xml_iis)
IISLog.Parse(_iislogs, e);
// IISW3C LOGS
IEnumerable<XElement> xml_iisw3c =
from el in inputs.Elements("IISW3CLogs").Elements("IISW3CLog")
select el;
foreach (XElement e in xml_iisw3c)
{
IISW3CLog.Parse(_iisw3clogs, e);
}
foreach (XElement e in xml_iisw3c)
IISW3CLog.Parse(_iisw3clogs, e);
}
static void parseConfFilter(string xmlConfFile)
@@ -178,16 +169,20 @@ namespace TimberWinR
{
switch (e.Name.ToString())
{
case DateFilter.TagName:
DateFilter.Parse(_filters, e);
break;
case GrokFilter.TagName:
GrokFilter.Parse(_filters, e);
break;
case MutateFilter.TagName:
MutateFilter.Parse(_filters, e);
break;
default:
throw new Exception(string.Format("Unknown tag: {0}", e.Name.ToString()));
break;
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml;
using Newtonsoft.Json.Linq;
using System.Xml.Linq;
@@ -10,6 +11,8 @@ namespace TimberWinR.Filters
{
public class DateFilter : FilterBase
{
public const string TagName = "Date";
public string Field { get; private set; }
public string Target { get; private set; }
public bool ConvertToUTC { get; private set; }
@@ -29,67 +32,13 @@ namespace TimberWinR.Filters
{
Patterns = new List<string>();
ParseField(parent);
ParseTarget(parent);
ParseConvertToUTC(parent);
Field = ParseStringAttribute(parent, "field");
Target = ParseStringAttribute(parent, "target", Field);
ConvertToUTC = ParseBoolAttribute(parent, "convertToUTC", false);
ParsePatterns(parent);
}
private void ParseField(XElement parent)
{
string attributeName = "field";
try
{
XAttribute a = parent.Attribute(attributeName);
Field = a.Value;
}
catch
{
}
}
private void ParseTarget(XElement parent)
{
string attributeName = "field";
try
{
XAttribute a = parent.Attribute(attributeName);
Field = a.Value;
}
catch
{
}
}
private void ParseConvertToUTC(XElement parent)
{
string attributeName = "convertToUTC";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
ConvertToUTC = true;
}
else if (value == "OFF" || value == "false")
{
ConvertToUTC = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParsePatterns(XElement parent)
{
foreach (var e in parent.Elements("Pattern"))
@@ -148,6 +97,5 @@ namespace TimberWinR.Filters
else
json[Target] = ts;
}
}
}

View File

@@ -2,12 +2,45 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Newtonsoft.Json.Linq;
namespace TimberWinR.Filters
{
public abstract class FilterBase
{
public abstract void Apply(JObject json);
public abstract void Apply(JObject json);
protected static string ParseStringAttribute(XElement e, string attributeName, string defaultValue="")
{
string retValue = defaultValue;
XAttribute a = e.Attribute(attributeName);
if (a != null)
retValue = a.Value;
return retValue;
}
protected static bool ParseBoolAttribute(XElement e, string attributeName, bool defaultValue)
{
bool retValue = defaultValue;
XAttribute a = e.Attribute(attributeName);
if (a != null)
{
switch (a.Value)
{
case "ON":
case "true":
retValue = true;
break;
case "OFF":
case "false":
retValue = false;
break;
}
}
return retValue;
}
}
}

View File

@@ -44,20 +44,9 @@ namespace TimberWinR.Filters
private void ParseMatch(XElement parent)
{
XElement e = parent.Element("Match");
if (e != null)
{
string attributeName = "value";
try
{
Match = e.Attribute(attributeName).Value;
}
catch
{
throw new TimberWinR.ConfigurationErrors.MissingRequiredAttributeException(e, attributeName);
}
}
Match = e.Attribute("value").Value;
Field = e.Attribute("field").Value;
}
private void ParseAddFields(XElement parent)
@@ -177,7 +166,7 @@ namespace TimberWinR.Filters
var namedCaptures = regex.MatchNamedCaptures(text);
foreach (string fieldName in namedCaptures.Keys)
{
AddOrModify(json, fieldName, namedCaptures[fieldName]);
AddOrModify(json, fieldName, namedCaptures[fieldName]);
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Xml.Linq;
@@ -52,5 +53,96 @@ namespace TimberWinR.Inputs
return fields;
}
protected static string ParseRequiredStringAttribute(XElement e, string attributeName)
{
XAttribute a = e.Attribute(attributeName);
if (a != null)
return a.Value;
else
throw new TimberWinR.ConfigurationErrors.MissingRequiredAttributeException(e, attributeName);
}
protected static string ParseStringAttribute(XElement e, string attributeName, string defaultValue = "")
{
string retValue = defaultValue;
XAttribute a = e.Attribute(attributeName);
if (a != null)
retValue = a.Value;
return retValue;
}
protected static bool ParseRequiredBoolAttribute(XElement e, string attributeName)
{
XAttribute a = e.Attribute(attributeName);
if (a == null)
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(e.Attribute(attributeName));
bool retValue = false;
switch (a.Value)
{
case "ON":
case "true":
return true;
case "OFF":
case "false":
return false;
break;
default:
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(e.Attribute(attributeName));
}
}
protected static string ParseEnumAttribute(XElement e, string attributeName, IEnumerable<string> values, string defaultValue)
{
XAttribute a = e.Attribute(attributeName);
if (a != null)
{
string v = a.Value;
if (values.Contains(v))
return v;
else
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(e.Attribute(attributeName));
}
return defaultValue;
}
protected static int ParseIntAttribute(XElement e, string attributeName, int defaultValue)
{
XAttribute a = e.Attribute(attributeName);
if (a != null)
{
int valInt;
if (int.TryParse(a.Value, out valInt))
return valInt;
else
throw new TimberWinR.ConfigurationErrors.InvalidAttributeIntegerValueException(a);
}
return defaultValue;
}
protected static bool ParseBoolAttribute(XElement e, string attributeName, bool defaultValue)
{
bool retValue = defaultValue;
XAttribute a = e.Attribute(attributeName);
if (a != null)
{
switch (a.Value)
{
case "ON":
case "true":
retValue = true;
break;
case "OFF":
case "false":
retValue = false;
break;
}
}
return retValue;
}
}
}

View File

@@ -14,8 +14,7 @@ namespace TimberWinR.Inputs
// Parameters
public int ICodepage { get; private set; }
public int Recurse { get; private set; }
public bool SplitLongLines { get; private set; }
public string ICheckpoint { get; private set; }
public bool SplitLongLines { get; private set; }
public static void Parse(List<TailFileInput> logs, XElement logElement)
{
@@ -29,139 +28,13 @@ namespace TimberWinR.Inputs
public TailFileInput(XElement parent)
{
ParseName(parent);
ParseLocation(parent);
// Default values for parameters.
ICodepage = 0;
Recurse = 0;
SplitLongLines = false;
ParseICodepage(parent);
ParseRecurse(parent);
ParseSplitLongLines(parent);
ParseICheckpoint(parent);
Name = ParseRequiredStringAttribute(parent, "name");
Location = ParseRequiredStringAttribute(parent, "location");
ICodepage = ParseIntAttribute(parent, "iCodepage", 0);
Recurse = ParseIntAttribute(parent, "recurse", 0);
SplitLongLines = ParseBoolAttribute(parent, "splitLongLines", false);
ParseFields(parent);
}
private void ParseName(XElement parent)
{
string attributeName = "name";
try
{
XAttribute a = parent.Attribute(attributeName);
Name = a.Value;
}
catch
{
throw new TimberWinR.ConfigurationErrors.MissingRequiredAttributeException(parent, attributeName);
}
}
private void ParseLocation(XElement parent)
{
string attributeName = "location";
try
{
XAttribute a = parent.Attribute(attributeName);
Location = a.Value;
}
catch
{
throw new TimberWinR.ConfigurationErrors.MissingRequiredAttributeException(parent, attributeName);
}
}
private void ParseICodepage(XElement parent)
{
string attributeName = "iCodepage";
int valInt;
try
{
XAttribute a = parent.Attribute(attributeName);
if (int.TryParse(a.Value, out valInt))
{
ICodepage = valInt;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeIntegerValueException(a);
}
}
catch
{
}
}
private void ParseRecurse(XElement parent)
{
string attributeName = "recurse";
int valInt;
try
{
XAttribute a = parent.Attribute(attributeName);
if (int.TryParse(a.Value, out valInt))
{
Recurse = valInt;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeIntegerValueException(a);
}
}
catch
{
}
}
private void ParseSplitLongLines(XElement parent)
{
string attributeName = "splitLongLines";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
SplitLongLines = true;
}
else if (value == "OFF" || value == "false")
{
SplitLongLines = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseICheckpoint(XElement parent)
{
string attributeName = "iCheckpoint";
try
{
XAttribute a = parent.Attribute(attributeName);
ICheckpoint = a.Value;
}
catch { }
}
}
private void ParseFields(XElement parent)
{
@@ -182,15 +55,12 @@ namespace TimberWinR.Inputs
sb.Append(String.Format("Name: {0}\n", Name));
sb.Append(String.Format("Location: {0}\n", Location));
sb.Append("Fields:\n");
foreach (FieldDefinition f in Fields)
{
sb.Append(String.Format("\t{0}\n", f.Name));
}
foreach (FieldDefinition f in Fields)
sb.Append(String.Format("\t{0}\n", f.Name));
sb.Append("Parameters:\n");
sb.Append(String.Format("\tiCodepage: {0}\n", ICodepage));
sb.Append(String.Format("\trecurse: {0}\n", Recurse));
sb.Append(String.Format("\tsplitLongLines: {0}\n", SplitLongLines));
sb.Append(String.Format("\tiCheckpoint: {0}\n", ICheckpoint));
sb.Append(String.Format("\tsplitLongLines: {0}\n", SplitLongLines));
return sb.ToString();
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using Microsoft.SqlServer.Server;
namespace TimberWinR.Inputs
{
@@ -33,237 +34,18 @@ namespace TimberWinR.Inputs
WindowsEvent(XElement parent)
{
ParseSource(parent);
// Default values for parameters.
FullText = true;
ResolveSIDS = true;
FormatMsg = true;
MsgErrorMode = "MSG";
FullEventCode = false;
Direction = "FW";
StringsSep = "|";
BinaryFormat = "PRINT";
ParseFullText(parent);
ParseResolveSIDS(parent);
ParseFormatMsg(parent);
ParseMsgErrorMode(parent);
ParseFullEventCode(parent);
ParseDirection(parent);
ParseStringsSep(parent);
ParseBinaryFormat(parent);
Source = ParseRequiredStringAttribute(parent, "source");
FullText = ParseBoolAttribute(parent, "fullText", true);
ResolveSIDS = ParseBoolAttribute(parent, "resolveSIDS", true);
FormatMsg = ParseBoolAttribute(parent, "formatMsg", true);
MsgErrorMode = ParseEnumAttribute(parent, "msgErrorMode", new string[] {"NULL", "ERROR", "MSG"}, "MSG");
FullEventCode = ParseBoolAttribute(parent, "fullEventCode", false); ;
Direction = ParseEnumAttribute(parent, "direction", new string[] { "FW", "BW" }, "FW");
StringsSep = ParseStringAttribute(parent, "stringsSep", "|");
BinaryFormat = ParseEnumAttribute(parent, "binaryFormat", new string[] { "ASC", "PRINT", "HEX" }, "PRINT");
ParseFields(parent);
}
private void ParseSource(XElement parent)
{
string attributeName = "source";
try
{
XAttribute a = parent.Attribute(attributeName);
Source = a.Value;
}
catch
{
throw new TimberWinR.ConfigurationErrors.MissingRequiredAttributeException(parent, attributeName);
}
}
private void ParseFullText(XElement parent)
{
string attributeName = "fullText";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
FullText = true;
}
else if (value == "OFF" || value == "false")
{
FullText = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseResolveSIDS(XElement parent)
{
string attributeName = "resolveSIDS";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
ResolveSIDS = true;
}
else if (value == "OFF" || value == "false")
{
ResolveSIDS = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseFormatMsg(XElement parent)
{
string attributeName = "formatMsg";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
FormatMsg = true;
}
else if (value == "OFF" || value == "false")
{
FormatMsg = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseMsgErrorMode(XElement parent)
{
string attributeName = "msgErrorMode";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "NULL" || value == "ERROR" || value == "MSG")
{
MsgErrorMode = value;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseFullEventCode(XElement parent)
{
string attributeName = "fullEventCode";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ON" || value == "true")
{
FullEventCode = true;
}
else if (value == "OFF" || value == "false")
{
FullEventCode = false;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseDirection(XElement parent)
{
string attributeName = "direction";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "FW" || value == "BW")
{
Direction = value;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseStringsSep(XElement parent)
{
string attributeName = "stringsSep";
try
{
XAttribute a = parent.Attribute(attributeName);
StringsSep = a.Value;
}
catch { }
}
private void ParseBinaryFormat(XElement parent)
{
string attributeName = "binaryFormat";
string value;
try
{
XAttribute a = parent.Attribute(attributeName);
value = a.Value;
if (value == "ASC" || value == "PRINT" || value == "HEX")
{
BinaryFormat = value;
}
else
{
throw new TimberWinR.ConfigurationErrors.InvalidAttributeValueException(parent.Attribute(attributeName));
}
}
catch { }
}
private void ParseFields(XElement parent)
{
Dictionary<string, Type> allPossibleFields = new Dictionary<string, Type>()

View File

@@ -4,7 +4,8 @@
<xs:sequence>
<xs:element name="Inputs">
<xs:complexType>
<xs:sequence>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:choice>
<xs:element name="WindowsEvents" minOccurs="0">
<xs:complexType>
<xs:sequence>
@@ -247,56 +248,96 @@
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Filters" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Grok" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Match" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="field" use="required"/>
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="AddField" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="DropIfMatch" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="RemoveField" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:choice>
<xs:element name="Grok" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="Match" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="field" use="required"/>
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="AddField" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="DropIfMatch" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="RemoveField" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Mutate">
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element name="Rename" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="oldName" use="optional"/>
<xs:attribute type="xs:string" name="newName" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="Remove" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute type="xs:string" name="field" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Date">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="Pattern" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
<xs:attribute type="xs:string" name="field" use="required"/>
<xs:attribute type="xs:string" name="target" use="optional"/>
<xs:attribute type="xs:string" name="convertToUTC" use="optional"/>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>