a trace of thought on...BizTalk Server, Team Foundation Server, Windows Mobile, etc. RSS 2.0
 Thursday, May 17, 2007

There is a new release of QuickCounters up at http://codeplex.com/quickcounters.  Great new useability additions to the viewer application by Dave Comfort, as well as fewer priviliges required against remote servers.  See here for the quick tour…

I’ll be talking about using QuickCounters with BizTalk at tonight’s Minnesota BizTalk User Group meeting.  (Last night, I was presenting on using Scrum with Team Foundation Server at the Minnesota VSTS User Group – it’s been a busy week!)

Thursday, May 17, 2007 1:48:19 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
BizTalk Tools
 Thursday, February 01, 2007

Another release of QuickCounters is available - check it out! Improved WCF support, and enhancements to the viewer. You really have to play with the library a bit to realize what a huge benefit it can provide both to operations and performance test investments (for fairly little investment on your part...)

Our CodePlex home is here. Downloads and source code here.

Thursday, February 01, 2007 10:56:13 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Thursday, December 14, 2006

Have you ever wanted to get real-time metrics on:  How many times has a particular orchestration executed on this node in the BizTalk group?  What does the average execution time for my pipeline component looking like?  How many instances of a particular orchestration are currently executing across the group, not including dehydrated instances?

Questions like this can be tough to answer - the built-in BizTalk counters are too coarse-grained, and Tracking & BAM are only partially useful here (particularly if you want high frequency samples.)  What you would like is the ability to get fine-grained "request metrics" on all of your BizTalk orchestrations and pipelines in a way that is simple to implement and easy to consume.

I've been leading an open source project called QuickCounters for quite some time now - though we've just recently moved into CodePlex.  It is a library that will be of interest to BizTalk developers - but it can be used in much broader settings.  I had a chance to present on this topic at the Twin Cities .NET Users Group (on 11/2/2006 - presentation here.)

QuickCounters is a .NET library which makes it extremely easy to implement the common, request-level performance counters that are interesting in just about any service you might write.  You can use this library to instrument  general .Net components, web services, BizTalk orchestrations, pipeline components, remoting interfaces, enterprise service components...you get the idea.  There is special support in the library for BizTalk and WCF scenarios, but it is quite easy to use in any case.

QuickCounters recognizes that the Windows Performance Counter technology that has been with us for some time is often still the best choice for providing (and consuming) detailed performance metrics.  QuickCounters also recognizes that for any given service request there are several metrics which turn out to be interesting for performance testing, operational health analysis, and historical trending. The idea with QuickCounters is to raise the level of abstraction from that of an individual performance counter up to the request itself, using a simple API.

Suppose you want to gather these metrics for each type of request in your system:

  • Requests Started
  • Requests Executing
  • Requests Completed
  • Requests Failed
  • Request Execution Time
  • Requests/Hour
  • Requests/Min
  • Requests/Sec

Although the .NET performance classes would give you a good start, it will still be a chunk of work.  With QuickCounters, you describe your requests in a simple xml format, and include a simple code snippet in each request implementation:

void SampleRequest()
{
   RequestType someRequest = RequestType.Attach("MyApplication","someRequest");
   someRequest.BeginRequest();

   try
   {
      // Do useful work...
      someRequest.SetComplete();
   }
   catch
   {
      someRequest.SetAbort();
      throw;
   }
}
        

After a quick "install" of the xml that describes your requests (via the included "QuickCounter viewer" utility) you are on your way.  You can see all eight metrics described above for all of your requests (which each become a Performance object) - in PerfMon, with MOM, or any other performance counter consumer. 

For a BizTalk orchestration, you will simply have a variable of type RequestType, which you will assign in an expression shape at the top of your orchestration via the static "RequestType.Attach" method, followed by a call to BeginRequest: 

quickCounterDemo = QuickCounters.RequestType.Attach(
    "QuickCounterDemo","demo",
    QuickCounterDemo(Microsoft.XLANGs.BaseTypes.InstanceId)); 
quickCounterDemo.BeginRequest();           

Successful completion should end with another expression shape that calls SetComplete, otherwise SetAbort.  (Full sample referenced below.)  And yes, the library understands that an orchestration's execution will often begin on one node in your BizTalk group and continue/complete on a different node. 

Of course, if you have many requests, and many nodes in your BizTalk group, it can be a hassle to add everything to PerfMon.  20 requests * 3 servers = 60 interactions with PerfMon's UI.  You might remember to save an .msc file, but you might not.  Here is where the QuickCounter viewer comes into play.  Since you've already written an xml file that describes your requests, just let the viewer know about that file -  and a comma separated list of servers you are running on:


(click)

(The viewer has benefitted substantially from John Thom's contributions...)

Now, the WCF support in the library is...amazing, and completely the result of contributions from Tomas Restrepo.  Keep an eye on his blog for a full discussion, but the bottom line is that creation of the simple xml format and all API calls (BeginRequest/SetComplete/SetAbort) are handled for you automatically simply by applying an attribute (or a behavior - your choice) to your WCF service implementation.  How cool is that?

Our CodePlex home is here.  Downloads and source code here.  Contributors here.

I expect we'll release about once a month or so for awhile - give it a spin and let us know what you think.  It is being used by some very large BizTalk projects (in production) with good success right now.

Thursday, December 14, 2006 4:53:19 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Friday, August 12, 2005

In my last entry, I discussed some ways that can making working with binding files a bit easier.  Here is another post in that same vein that addresses a common pain point...

Un-escaping TransportTypeData

One of the annoying things about binding files is that adapters only have a string element available to store adapter-specific information for send ports and receive locations.  As a result, adapters will store escaped XML (or even "doubly escaped" xml...)  This can be extremely hard to manage, especially for adapters such as MQSeries that keep quite a bit of information in this form. 

To solve this problem, I introduced a new command-line tool in the most recent version of the Deployment Framework called "ElementTunnel.exe" (the source for which is in the Tools download.)  This utility will take in an xml file, along with a file containing xpaths to elements that should be "encoded" or "decoded".  The end result is that you can choose to manage a "master" binding file (not directly useable) and run ElementTunnel on it immediately prior to deployment.  (You may also run XmlPreProcess on the same file for macro expansion! The sample in the deployment framework shows both occurring - XmlPreProcess should occur first!) 

So what does this mean?  An example for a single Send Port snippet: It means that, in the case of MQSeries, instead of storing and maintaining this mess:

<SendPort Name="SomeQueue" IsStatic="true" IsTwoWay="false">
<TransmitPipeline Name="SomeAssembly.SomeQueue" FullyQualifiedName="SomeAssembly.SomeQueue,
SomeAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bb955d799cc915b9" Type="2" />
<PrimaryTransport>
<Address>MQS://SomeServer/SomeQM/SomeQueue</Address>
<TransportTypeData>&lt;CustomProps&gt;&lt;AdapterConfig vt="8"&gt;&amp;lt;Config xmlns:xsd=
;"http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&
amp;gt;&amp;lt;uri&amp;gt;MQS://SomeServer/SomeQM/ SomeQueue& amp;lt;/uri&amp;gt;&amp;lt;queueDetails&
amp;gt;SomeServer/SomeQM/SomeQueue&amp;lt;/queueDetails&
amp;gt;&amp;lt;transactionSupported&amp;gt;yes&
amp;lt;/transactionSupported&amp;gt;&amp;lt;dataConversion&amp;gt;no&amp;lt;/dataConversion&
amp;gt;&amp;lt;segmentationAllowed&amp;gt;no&amp;lt;
/segmentationAllowed&amp;gt;&amp;lt;fragmentationSize&
amp;gt;500&amp;lt;/fragmentationSize&amp;gt;&amp;lt;ordered& amp;gt;no&amp;lt;/ordered&amp;gt;&amp;lt;/Config&
amp;gt;&lt;/AdapterConfig& gt;&lt;/CustomProps&gt;</TransportTypeData>
<RetryCount>3</RetryCount>
<RetryInterval>5</RetryInterval>
...
</SendPort> 

You can store and maintain this:

<SendPort Name="SomeQueue" IsStatic="true" IsTwoWay="false">
<TransmitPipeline Name="SomeAssembly.SomeQueue" FullyQualifiedName="SomeAssembly.SomeQueue,
SomeAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bb955d799cc915b9" Type="2" />
<PrimaryTransport>
<Address>MQS://SomeServer/SomeQM/SomeQueue</Address>
<TransportTypeData>     <CustomProps>
        <AdapterConfig vt="8">
            <Config>
                <uri>MQS://SomeServer/SomeQM/SomeQueue</uri>
                <queueDetails>SomeServer/SomeQM/SomeQueue</queueDetails>
                <transactionSupported>yes</transactionSupported>
                <dataConversion>no</dataConversion>
                <segmentationAllowed>no</segmentationAllowed>
                <fragmentationSize>500</fragmentationSize>
                <ordered>no</ordered>
            </Config>
        </AdapterConfig>
    </CustomProps> </TransportTypeData>
<RetryCount>3</RetryCount>
<RetryInterval>5</RetryInterval>
...
</SendPort> 

Ahhh, isn't that better?  Of course, similar goodness for all other adapters.  And, in the clean version, you'll find it easier to place/maintain XmlPreProcess macros. 

In the Deployment Framework sample, you'll see that we pass the following xpaths to ElementTunnel (along with the "master" binding file itself):

/BindingInfo/ReceivePortCollection/ReceivePort/ReceiveLocations/ReceiveLocation/ ReceiveLocationTransportTypeData/CustomProps/AdapterConfig
/BindingInfo/ReceivePortCollection/ReceivePort/ReceiveLocations/ReceiveLocation/ ReceiveLocationTransportTypeData
/BindingInfo/SendPortCollection/SendPort/*/TransportTypeData/CustomProps/AdapterConfig
/BindingInfo/SendPortCollection/SendPort/*/TransportTypeData


If you want to "unescape" your binding file (generally a one-time thing, just to get clean content) you'll want to pass these xpaths in a slightly different order, because of the "double escaping":

/BindingInfo/ReceivePortCollection/ReceivePort/ReceiveLocations/ReceiveLocation/ ReceiveLocationTransportTypeData
/BindingInfo/ReceivePortCollection/ReceivePort/ReceiveLocations/ReceiveLocation/ ReceiveLocationTransportTypeData/CustomProps/AdapterConfig
/BindingInfo/SendPortCollection/SendPort/*/TransportTypeData
/BindingInfo/SendPortCollection/SendPort/*/TransportTypeData/CustomProps/AdapterConfig


So! If you are managing large binding files (where escaped xml is getting in your way), you might find this technique handy...Grab the tool, and give it a go.
Friday, August 12, 2005 12:32:47 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Wednesday, May 11, 2005

There have been several folks who have discussed how to create messages "from scratch" within an Orchestration context - you can read Matt's thoughts and check out a BizTalk documentation excerpt. This is a common question, and I had an additional technique I thought I would share... 

Background: When you have a schema that has many promoted properties (or distinguished fields), or many elements that can be set via xpath expressions easily, it can be useful to simply start with a "template" document instance and populate the element content that you are interested in. 

In this situation, you will often have a "Message Assignment" shape that looks something like this:

xmlDoc.LoadXml("<ns0:BizTalkSampleS3 
      xmlns:ns0="http://BizTalkSample.Schemas.BizTalkSampleS3">
      <SomeElement></SomeElement></ns0:BizTalkSampleS3>");
someMsg = xmlDoc;
someMsg.SomeElement = "some content";
// (or xpath(someMsg,someElementXPath) = "some content" if we don't have a 
// distinguished field.)
   

One disadvantage of loading up "template" xml documents from either expression shapes or code (via XmlDocument.LoadXml) is that those xml fragments can get easily "lost", and are hard to update early in the development cycle when schemas may still be in flux.  Loading the template files from the file system is problematic because the question arises "where should I store these files, so that I can find them in any environment I deploy to?" (Solvable, but a hassle.)

Instead, why not embed the template xml documents as assembly resources?  For those unfamiliar with that process, I have a short tutorial here (& a helper class.) 

  1. You will need a C# project as part of your overall BizTalk solution.  Place your template xml file(s) in the directory corresponding to this project, and add them as an "existing item" to the project. 
  2. Select this file within the Solution Explorer, and within the Properties window, select "Embedded Resource" as the "Build Action" as shown here:

     

  3. Place this class (text here) within the same C# project that houses the resources you have added.
  4. To construct a message, drag out a "Message Assignment" shape, and within the associated expression write some code like the following.  Simply pass the file name of the template document as an argument to GetXmlDocResource (or GetStringResource.)
    sampleUsingTemplate = 
       BizTalkSample.Components.EmbeddedResourceCache.
          GetXmlDocResource("BizTalkSampleS3_output.xml");
       // Populate the "rest" of the message with distinguished fields, promoted 
       // properties, xpath expressions, etc.
       sampleUsingTemplate.SomeElement = "foo";
                

The class I have supplied will cache the loaded resources in a hashtable for performance sake, and allow you to load resources as both strings and XmlDocuments.

A last thought: Many people ask, "Why can't I just create a message using a new operator or a default constructor of some sort?" Well, because few XSD schemas sufficiently constrain the set of valid instance documents enough for that to be useful - what form would a "default message" take? (Would it have the optional elements you need? Some elements that you don't want?)

Enjoy - feedback appreciated!

Wednesday, May 11, 2005 3:29:14 PM (Central Standard Time, UTC-06:00)  #    Comments [6] -
BizTalk Tools | BizTalk Insights
 Wednesday, January 19, 2005

(Note - this work has been updated for log4net 1.2.9. See the 'Tools' download in the Deployment Framework)

Early on when working with BizTalk 2004, it might be tempting to view the ability to track orchestration events (and the use of the orchestration debugger) as a substitute for traditional diagnostic logging.;

After all, the orchestration debugger can tell you exactly how far you got in an orchestration, and what path through the workflow you took to get there

However, consider these limitations:

  • The orchestration debugger is used either a) after the orchestration has completed or b) in conjunction with a breakpoint.  Neither of these choices is ideal, and only the latter gives you visibility into the intermediate values of variables/messages.
  • Once an orchestration involves looping constructs, multiple orchestrations connected via messaging, etc. the orchestration debugger often loses quite a bit of diagnostic value.

Given this, it can be useful to integrate diagnostic logging into your orchestrations and the components they call.  If we are going to invest in this effort, we’d like to use something richer than the built-in System.Diagnostics.Trace/Debug infrastructure.

Log4net is an Apache-sponsored initiative within the “Apache Logging Services” project.  It provides a rich diagnostic infrastructure for .NET, with support for hierarchical logging and configuration, multiple logging targets, and support for logging context.

A recent article outlined the advantages of log4net over Microsoft’s Enterprise Instrumentation Framework (EIF), though Microsoft/Avanade will be revamping this logging infrastructure with the release of the Enterprise Template Library

Given that the log4net initiative has broad support at the API layer (i.e. log4j, log4PLSQL, etc.) and platform layer (including .NET CF) as well as the benefit of maturity, log4net seems like a very reasonable choice for instrumenting BizTalk 2004 applications.  (The Enterprise Template Library logging component will likely be a good choice as well when released.)

Following is a discussion of log4net itself, along with what is required to use it in a BizTalk 2004 setting.

Quick Insight into log4net 

Without diving immediately into the log4net API, let’s get a sense for what it can do.  What do we mean by hierarchical logging and configuration with multiple target support?

Suppose we have classes with these namespace-qualified names:

MyCompany.MyDepartment.MyClassA
MyCompany.MyDepartment.MyClassB

Each of these classes can obtain a logger specific to their class by declaring a static member as such:

private static readonly ILog log = LogManager.GetLogger(typeof(Foo));

Each class (MyClassA/MyClassB) can now emit trace with statements such as log.Info(“hello world”)> or log.Warn(“a potential problem…”).  Default log levels include: Debug, Info, Warn, Error, and Fatal (though these can be expanded.) 

The concept of “context” can be added to a stream of trace messages via NDC.Push(“some context”)/NDC.Pop or MDC.Set(“somekey”,”somevalue”).  (These class names are short for “Nested Diagnostic Context” and “Mapped Diagnostic Context” respectively.)  These can be extremely useful for multithreaded/multi-user applications, to distinguish the streams of related trace messages from each other.  This context can be optionally formatted into the associated tracing statements, as will be shown shortly.

If we want to turn on tracing at the ‘Error’ level by default, at the ‘Warn’ level for MyCompany, and at the ‘Debug’ level for MyClassA, we would simply configure as follows (notice the use of the namespace hierarchy):

<root>
   <level value="ERROR" />
</root>
<logger name="MyCompany">
   <level value="WARN" />
</logger>
<logger name="MyCompany.MyDepartment.MyClassA">
   <level value="DEBUG" />
</logger> 

This can be changed on-demand simply by changing the configuration file, which can be reloaded automatically without restarting the application.  This allows us to a) not be blinded by too much trace information and b) not impose undue server load by tracing, whereas a non-hierarchical approach only allows for one logging level for an entire application. 

Suppose it is desired to route all trace information (by default) to OutputDebugString, where a utility such as dbmon.exe or DebugView can be used to view it.  Suppose further it is desired to prefix our output with the date, thread Id, logging level, the name of the logger (i.e. MyCompany.MyDepartment.MyClassA), and the current context.  To do this with log4net, we would have our configuration file declare an “appender” of the appropriate type, and include a declaration of a “layout” as follows.  The “appender” is then referenced by a particular logger (or the root logger).

<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender" >
   <layout type="log4net.Layout.PatternLayout">
      <!-- The format specifiers let us add a wide variety of additional info -->
      <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />        
   </layout>
</appender>
<root>
   <level value="ERROR" />
   <appender-ref ref="OutputDebugStringAppender" />
</root> 

This might yield trace output as follows:

16:39:33,022 [316] INFO  MyCompany.MyDepartment.MyClassA [SomeContext] - Hello World

Again, the specifics of what additional information can be included in trace output can all be configured dynamically at runtime.  Note that other useful information such as method name, file name, line number, caller’s window identity can all be added with the PatternLayout class as well, though it is quite expensive (further adding value to the hierarchy concept!) 

Moreover, each element of the hierarchy can be directed to a different appender, or to multiple appenders.  Built in appenders include support for ADO.NET, .NET remoting, SMTP, files, rolling files, buffering, and the Event Log (among others.)  Because the Event Log is supported, there is no need to use a separate API to support such logging. 

Core log4net Concepts 

Without attempting to present a full tutorial (see this introduction for a more complete discussion), the core concepts in log4net include: 

Loggers: Use by code to emit trace output at various levels.

  • Loggers are organized into hierarchies just like .NET namespaces (they don't have to correspond, but often should.)  Example: System is parent of System.Text, and an ancestor of System.Text.StringBuilder. 
    • It is typical to have the logger name equal to a fully-qualified class name. 
  • Loggers are retrieved using a static method of LogManager, e.g. LogManager.GetLogger(someStringOrTypeName);
    • An ILog interface is returned with Debug, Info, Warn, Error, Fatal methods on it for tracing. 
  • Logging level is generally inherited from the hierarchy, and be configured anywhere in the hierarchy. 
    • Rule: logging level is equal to first non-null value working UP the tree.
    • Predefined levels: DEBUG, INFO, WARN, ERROR, FATAL, ALL, OFF
    • Default level for root in hierarchy is DEBUG 

Appenders: An output destination for logging. 

  • Built in: event log, asp.net trace, console, file, rolling file, smtp, etc.
  • Appenders can optionally have "layouts" to specialize how formatting is done and add additional information (windows identity, source code info, context, thread id, timestamp, etc.) 
  • Appenders are attached to a logger.  Log requests will go to a given logger's appender, as well as all appenders attached up the hierarchy (unless "additivity=false" at some level, in which case moving up hierarchy stops…) 

Filters: A given appender can apply a filter such that only log requests that match some criteria will actually be output.  Filters are applied sequentially.  Built-in filters include string match and regex match. 

Configurators: Classes which initialize the log4net environment.  Configuration is done either through assembly attributes that specify a config file, or an explicit call to specify a config file.  The config file can be a standard .net config file, or standalone.  Configuration is by default at the appdomain level, but can be finer-grained through the use of log4net "logging domains".

Using log4net with BizTalk 2004

There are a few challenges to using log4net in a BizTalk 2004 environment.  They can be summarized as follows: 

  • Assembly-attribute-driven configuration won’t work because BizTalk 2004 does not appear to support the addition of custom attributes for BizTalk assemblies.  (And even if it did, this method would pose issues for BizTalk environments.)  
  • Using the log4net configuration classes for configuration is problematic because there is no clear point at which to make the call.  (How does as an orchestration know if it is “first” and should initialize?  How does it know at any point in an orchestration whether the BizTalk service has been recently recycled, losing the log4net configuration?)  Moreover, there is not a good way to identify where the configuration file should be located.
  • ILog-derived classes (log4net loggers) are not serializable “out-of-the-box”, making it difficult to use them in an orchestration setting.
  • Log4net context-storage mechanisms are thread-relative, which isn’t workable for orchestrations, since orchestrations often a) dehydrate and then re-hydrate on a different thread and b) make use of parallel branches.  We’d like to correctly associate context such as the orchestration instance ID on a logger that is scoped to an orchestration. 

To address these challenges, I’ve created extensions to log4net that attempt to stay within log4net’s design intent.  These are housed in an assembly called “log4net.Ext.Serializable”.  (Update – these are now currently found in the “Extra Tools” download for the Deployment Framework – see the download links at the blog home page.)

The main classes defined are as follows: 

SLog: A serializable implementation of the ILog interface, to be used by orchestrations (or other components) for logging.  This class has an InstanceId property as well as a general properties collection that is carried with the logger itself.  (These can be made available in appender output via a PatternLayout class and the %P{yourpropname} format specifier.  Use %P{InstanceId} for the InstanceId.)  When deserialized, this class can detect an un-initialized state for log4net and recreate the correct configuration. 

SLogManager: To be used in place of log4net’s LogManager to dispense SLog classes.  This is the accepted way of dispensing custom loggers in log4net’s design. 

RegistryConfigurator: A class which consults a registry key for the location of a log4net config file, and creates the configuration in the specified “logging domain” using the log4net DomConfigurator class. 

Specifics for BizTalk 2004 Usage 

Using the assembly just discussed, the specifics of using log4net with a particular BizTalk project can be described as follows: 

  • Get 1.2.0 Beta8 of log4net at log4net extension described above, as well as a full sample project if you like.  (Update – the current version of this sample will now always be found in the Deployment Framework sample – see download links at the blog home page.)
  • If you are using NAnt to deploy your BizTalk solution, extend your customDeployTarget as shown below to create a registry key that will point to a log4net configuration file within your project’s root directory (whether on a developer desktop or an MSI base directory). (Handled for you in current rev of deployment framework.) Also, create a file with the log4net extension that corresponds to your solution name (i.e. BizTalkSample.log4net)

    <target name="customDeployTarget"> 
       <!-- Write registry key with location of our log4net configuration file.  This step is NOT needed in current rev of Deployment Framework… -->
       <exec program="cscript.exe" failonerror="true" commandline="/nologo ${deployTools}\WriteRegValue.vbs HKLM\SOFTWARE\${nant.project.name}\log4netConfig ${nant.project.basedir}\${nant.project.name}.log4net" />
    </target>
  • Orchestration assemblies should reference log4net.Ext.Serializable as well as log4net.dll, and declare a variable in each orchestration (perhaps named “logger”) of type log4net.Ext.Serializable.SLog.
  • Orchestrations should begin (after an activating receive, if necessary) with an expression shape that looks like:

    logger = log4net.Ext.Serializable.SLogManager.GetLogger(@"BizTalkSample",log4net.helpers.CallersTypeName.Name);
    logger.RegistryConfigurator();
    logger.InstanceId = MyOrchName(Microsoft.XLANGs.BaseTypes.InstanceId);
    logger.Debug("New request arrived...");

    Important: If an orchestration has parallel branches, you will want to declare multiple variables of type log4net.Ext.Serializable.SLog, assigning 2-n to the first one, as in:

    logger2 = logger;

    This prevents the need for synchronizing scopes.  (See this article for more.)&nbps;An alternative is to use scope-local logger variables that are initialized by assigning them to a global instance.

  • At various points in your orchestration, simply make calls to logger.Debug/logger.Info/logger.Error, etc.  Rely on logger.Error for event log entries (with appropriate logger/appender configuration.)
  • Use log4net with the components that your orchestration calls as well.  If those components are serializable, they will want to make use of SLogManager/SLog classes as well in non-static methods.  If you want context (such as the orchestration instance ID) to flow, it might be worthwhile to make an SLog-typed property visible on the class that the orchestration can set.  (This needs more thought.)

Using log4net with Paul Bunyan 

Paul Bunyan is a logging tool that has a very nice server-side buffering service as well as a great trace-viewing client that can connect to multiple servers simultaneously (a great plus for BizTalk groups!)  Although Paul Bunyan has its own API, we aren’t interested in it for purposes of this discussion.

A log4net appender can be easily written for Paul Bunyan, such that log output can be directed there.  Moreover, this appender can be made to explicitly recognize an InstanceId property carried with a log4net logging event, and map it to the “Context” concept within Paul Bunyan!  This allows for viewing the orchestration instance ID within a distinct, filterable column in the Paul Bunyan viewer as such:

Paul Bunyan View
(click)

Sample .log4net configuration File 

Note that types have to be referred to with fully-qualified names since log4net.dll is being deployed to and loaded from the GAC.

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

   <!-- Define some output appenders -->
   <appender name="OutputDebugString" type="log4net.Appender.OutputDebugStringAppender,
        log4net,Version=1.2.0.30714,Culture=Neutral,PublicKeyToken=b32731d11ce58905">
      <layout type="log4net.Layout.PatternLayout,log4net,
           Version=1.2.0.30714,Culture=Neutral,PublicKeyToken=b32731d11ce58905">
         <param name="ConversionPattern" value="%d [%t] %-5p %c - %m [%P{InstanceId}]%n" />
      </layout>
   </appender>

   <appender name="EventLog" type="log4net.Appender.EventLogAppender, log4net, 
        Version=1.2.0.30714, Culture=Neutral, PublicKeyToken=b32731d11ce58905">
      <param name="ApplicationName" value="BizTalkSample" />
      <layout type="log4net.Layout.PatternLayout,log4net,
           Version=1.2.0.30714,Culture=Neutral,PublicKeyToken=b32731d11ce58905">
         <param name="ConversionPattern" value="[%t] %-5p %c - %m [%P{InstanceId}]%n" />
      </layout>
      <!-- Only send error/fatal messages to the event log. -->
      <filter type="log4net.Filter.LevelRangeFilter">
         <param name="LevelMin" value="ERROR" />
         <param name="LevelMax" value="FATAL" />
      </filter>
   </appender>

   <!-- Setup the root category, add the appenders and set the default level -->
   <root>
      <!-- Possible levels (in order of increasing detail):
                     OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL -->
      <level value="ERROR" />
      <appender-ref ref="EventLog" />
   </root>

   <logger name="BizTalkSample">
      <level value="ALL" />
      <appender-ref ref="OutputDebugString" />
   </logger>
   
</log4net>
Wednesday, January 19, 2005 11:43:06 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Tuesday, December 07, 2004

Some time ago, I published a set of proposed naming conventions for BizTalk 2004.  

In the interim, I’ve had a chance to see these used in a few different projects and to review them with several people -- and they have evolved & grown a fair bit.  

So I thought I’d publish the new version - the Word version can be found here, and an HTML version can be found here.  (I’ve found it helpful to open the HTML version directly in VS.NET for quick reference.)

Hopefully these will be of value to your project.  I’ve certainly found that the documentation value to be had within Orchestrations and within the operational tools can be hugely improved by following these conventions – especially if you are using the UK SDC BizTalk 2004 Documenter tool (you are, aren’t you?)  My comments on the use of such documentation in my old post still apply, I believe.

Leave comments with questions or suggestions…

Tuesday, December 07, 2004 2:16:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Friday, October 29, 2004

It can turn out to be useful to use Health and Activity Tracking (HAT) and the BizTalk Server Administration Console “remotely” (that is, from a machine other than one of the actual servers in the BizTalk group.)  This can be the case if you have multiple environments to administer, and/or using Remote Desktop is not desirable or available.

It should be noted that to accomplish this, you technically have to have the “administrating machine” join the BizTalk Group – though since you won’t have any host instances defined on the machine, that isn’t as significant as it sounds.  You are essentially just configuring the machine to point at a particular BizTalk management database, and configuring some WMI information.  The “administrating machine” does not appear in the “Servers” node of the BizTalk Administration Console.

It should also be noted that you will need to be in the “BizTalk Administrators” Windows group for the BizTalk environment you want to manage. 

To get started, the “administrator” should do an “Admin-only” install of BizTalk on the machine they will be using, where the installation options for BizTalk look like this:

Then, on the desktop, the administrator should put a shortcut to a script that looks like the script below.  This script simply reminds the user what BizTalk Group they are currently administrating, and confirms they want to switch:

If you select OK, you get another warning:

This is basically warning someone who has a full-blown BizTalk installation on their machine that they really don’t want to remove their current configuration unless they happen to have a saved configuration file from their last run of ConfigFramework.exe.

If you select OK, the “ConfigFramework.exe” utility is run (from BizTalk’s installation directory) with the /u switch, to remove the current configuration.  Afterwards, ConfigFramework.exe is run normally, and the only option you will have (for an admin-only install of BizTalk) is to select the database corresponding to the environment you wish to administer:

Once you hit “Next”, the wizard will complete, and you will be presented with a final confirmation dialog:

 

The VBScript as a text file is here.

Friday, October 29, 2004 9:49:33 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
 Monday, March 29, 2004

(See here for the current version of the naming conventions!)

One of the primary benefits of the BizTalk Orchestration model is the great transparency you can get when a software implementation is pictorial.

Regardless of how well a developer comments code, there will always be a need to maintain a separate set of artifacts (UML diagrams, Visio diagrams with random shapes of your choosing, prose documents, whiteboard discussions, etc.) that are used to convey what the code is actually doing especially when working with a business audience.  This is true if for no other reason than that a discussion of interesting functionality will often take place at a different level of granularity than raw code can support

Round-trip engineering tools - that attempt to keep code in sync with diagrams often seem to suffer from a lack of fidelity that renders them ineffective.

With BizTalk Orchestration, the diagram is the implementation (at least at a particular level) of a piece of functionality.  Yes, you can disappear into components and lose sight of what might happen.  Yes, there is a code representation (xlang/s) underneath the orchestration but it seems to be completely isomorphic with the diagram.

So the opportunity exists to use an orchestration diagram in several interesting ways within a project lifecycle:

  1. As a way to capture an initial high-level design, using all the orchestration shapes as intended but not yet bothering with real maps and schemas.  Stubbing out schemas (so you can declare messages and variables to be of proper types) and maps will allow you to flesh out the orchestration diagram(s) quite a bit, using the compiler as just a way to check for consistency.  All of the external system interactions, communication patterns, decision points, parallel vs. joined flows, etc. can be represented at this point in a shell orchestration.
  2. As a way to gain consensus with the development team & business sponsor about whether the right functionality is indeed going to be built.  The high level design just described is a great tool for this discussion.  Put your orchestration(s) up on a wall with a projector and do a walk-through with as many of the project stakeholders as makes sense.  Or use a tool like CutePDF to print the orchestration as a PDF to send around via email.  (Of course, once Microsoft ships the Visio add-on for BizTalk 2004 orchestrations, this will represent another option for non-VS.NET users.  This has the added benefit of allowing you to exclude what you might consider to be lower-level detail by setting the Report to Analyst switch on various orchestration shapes to False.)
  3. As a way to estimate work.  The various shapes in your initial orchestration can often represent reasonable granularity for time estimates.
  4. And finally, as a way to guide project work...Rather than starting with the entire orchestration that you created to support steps 1-3, you might find it easier to create a new orchestration that represents the path(s) you are tackling at a particular point.  You can cut/paste portions of that original orchestration or simply use it as a reference for what comes next it serves as your outline.

To help realize some of these benefits, naming conventions within an orchestration are quite important

While the naming conventions are good practice for variables, Messages, Multi-Part types, etc. they are even more import for the workflow shapes.  The goal is to ensure that the intent of each shape is clear, and that the text associated with the shape conveys as much as possible given the space constraints.  In this way, a non-technical audience will be able to use the orchestration as documentation.

(See here for the current version of the naming conventions.)

Respond with comments & the document will remain updated per your feedback!

Monday, March 29, 2004 11:21:29 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Tools
Archive
<May 2008>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
About the author:

Scott Colestock lives, writes, and works as an independent consultant in the Twin Cities (Minneapolis, Minnesota) area.

© Copyright 2008
Scott Colestock
Sign In
All Content © 2008, Scott Colestock
DasBlog theme 'Business' created by Christoph De Baene (delarou)