跟踪和'设备未准备好'

时间:2012-03-12 02:00:30

标签: c# trace win32exception

我编写了一个非常简单的应用程序,它接受XML文件中的批量遥测日志行,并将它们转录到SQL Server上的表中。这些输入数据块中的每一个都称为“章节”。

出于性能原因,每个章节都包含在一个事务中,因此,对于100个新行,只有一个事务,而不是一百个隐式事务。

不幸的是,存在重复,并且由于共享的事务上下文,它们取出了整章。这种情况并不常见,但并不罕见。所以我添加了一个循环,一个重试标志和一个维护跳过列表的catch(SqlException)。当插入行barfs时,我回滚事务,创建一个新事务,将行号添加到跳过列表,设置重试标志并让它循环。除了跳过列出的行之外,所有行都被重新处理。如果是第二行barfs,除了skip-list有两个项目之外,会发生同样的事情。

这整个安排都有效。这是给我咖喱的最后一场比赛。当章节被端到端地处理而没有任何例外时,循环退出然后我检查跳过列表是否为空。当不是为空时,我尝试使用Trace.TraceWarning()编写一个事件日志条目,详细说明失败的行和XML块以供以后取证。

正是在这一点上事情向南发展。此跟踪语句barfs具有声称“设备未就绪”的Win32异常。

有没有人见过这样的东西?

一夜之间,我注意到偶尔会无怨无悔。我已经在Trace.TraceWarning()之前插入了一个Thread.Sleep(),所以看看它是否会将其排序会很有趣。我应该补充一点,我注意到消息已成功记录到Visual Studio的跟踪侦听器中,这使我认为它的速度或时间相关。

一天后,很明显睡觉线程没有区别。我重写了整个业务,以便StringBuilder累积状态,并在循环退出后发生单个trace语句。即使多行被拒绝需要两次以上的传递,Win32Exception也不再明显。究竟是什么导致这个特殊的例外仍然不清楚。我希望有人可以将光投射到这个黑暗的角落,但我没有进一步补充,因为每章的单个日志条目是理想的;即使没有Win32Exception,“替代方法”也是对代码的改进。

说得太快了。邪恶的Win32Exception又回来了。这是代码:

public void Write(string xml)
{
  StringBuilder sb = new StringBuilder();
  //Trace.TraceInformation(xml);
  SqlConnection cxn = new SqlConnection(Properties.Settings.Default.DatabaseBob);
  cxn.Open();
  SqlTransaction txn = null;
  SqlCommand cmd = new SqlCommand("INSERT INTO LOG(MAC, snip more fields ) VALUES(@MAC, snip more params)", cxn);
  var P = cmd.Parameters;
  P.Add(new SqlParameter("@MAC", SqlDbType.BigInt));

  /*snip define more SQL params*/

  XmlDocument doc = new XmlDocument();
  bool barfed = false;
  List<int> skipList = new List<int>();
  _stopwatch.Start();
  int i = 0;
  doc.LoadXml(xml);
  var root = doc.FirstChild.NextSibling;
  P["@MAC"].Value = ulong.Parse(root.Attributes["MAC"].Value, System.Globalization.NumberStyles.AllowHexSpecifier);
  P["@D2"].Value = P["@D3"].Value = 0; //COM2 
  do
    try
    {
      cmd.Transaction = txn = cxn.BeginTransaction();
      i = 0;
      barfed = false;
      for (var n = root.FirstChild; n != null; n = n.NextSibling)
      {
        foreach (XmlAttribute attr in n.Attributes)
        {
          var p = P["@" + attr.Name];
          switch (p.SqlDbType)
          {
            case SqlDbType.DateTime:
              p.Value = DateTime.Parse(attr.Value).ToUniversalTime();
              break;
            case SqlDbType.Float:
              p.Value = float.Parse(attr.Value);
              break;
            case SqlDbType.Bit:
              p.Value = Convert.ToBoolean(int.Parse(attr.Value));
              break;
          }
        }
        i++;
        if (!skipList.Contains(i))
          if ((DateTime)P["@GPSTIME"].Value > DateTime.UtcNow.AddMinutes(1))
          {
            sb.AppendFormat("Node {0} is being skipped because it has a future date.\r\n", i);
            skipList.Add(i);
          }
          else
            cmd.ExecuteNonQuery();
      }
      txn.Commit();
      sb.AppendFormat("{0} logs per {1}ms", i, _stopwatch.ElapsedMilliseconds);
    }
    catch (SqlException)
    {
      sb.AppendFormat("Node {0} is being skipped because it is a duplicate.\r\n", i);
      txn.Rollback();
      skipList.Add(i);
      barfed = true;
    }
    catch (Exception ex)
    {
      sb.AppendLine(ex.Message);
      txn.Rollback();
    }
  while (barfed);
  _stopwatch.Reset();
  if (skipList.Count == 0)
    Trace.TraceInformation(sb.ToString());
  else
  {
    sb.AppendLine();
    sb.Append(xml);
    Trace.TraceWarning(sb.ToString());
  }
}

这是异常的堆栈跟踪:

at System.Diagnostics.EventLogInternal.InternalWriteEvent(UInt32 eventID, UInt16 category, EventLogEntryType type, String[] strings, Byte[] rawData, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEvent(EventInstance instance, Byte[] data, Object[] values)
at System.Diagnostics.EventLog.WriteEvent(EventInstance instance, Object[] values)
at System.Diagnostics.EventLogTraceListener.TraceEvent(TraceEventCache eventCache, String source, TraceEventType severity, Int32 id, String message)
at System.Diagnostics.TraceInternal.TraceEvent(TraceEventType eventType, Int32 id, String format, Object[] args)
at System.Diagnostics.Trace.TraceWarning(String message)
at Bob.ChapterWriter.Write(String xml) in I:\Project Bob\LogWriter\ChapterWriter.cs:line 179
at SyncInvokeWrite(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)

WCF在响应MSMQ消息到达时调用Write方法。消息的内容是XML字符串。删除事件日志侦听器会使问题消失(到目前为止)。

对于那些检查堆栈跟踪的人,第179行位于粘贴代码的末尾。这又是:

Trace.TraceWarning(sb.ToString()); 

我想到了一个想法:我想知道是因为XML使消息对事件日志来说太大了吗?我已经有一个限制章节文件大小的设置。我会尝试减少它,看看会发生什么。

1 个答案:

答案 0 :(得分:1)

是的,真正的问题是超大的消息文本。事件日志条目的最大大小为64K,包括所有Microsoft标头和诸如此类的内容。我知道,但我没有想到这可能是问题因为我将XML限制在32K。它太多的原因是:当你将XML放入将嵌入XML的文本中时,它会被转义,这会使每个非法字符的大小增加四倍。我的所有尖括号和双引号的大小都是四倍。而且有很多。

这样做的结果是:

  • 不要在事件日志消息中放入大块XML。
  • 如果从Trace到事件日志获得Win32Exception,则您的消息可能太大了。
  • 避免此限制的一种方法是使用文本文件跟踪侦听器。