<?xml version="1.0" encoding="iso-8859-1"?><!-- generator="b2evolution/4.0.3" -->
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Enterprise Developer - Author(s): Eli Weinstock-Herman (tarwn)</title>
		<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/</link>
		<atom:link rel="self" type="application/rss+xml" href="http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2" />
		<description></description>
		<language>en-GB</language>
		<docs>http://blogs.law.harvard.edu/tech/rss</docs>
		<admin:generatorAgent rdf:resource="http://b2evolution.net/?v=4.0.3"/>
		<ttl>60</ttl>
				<item>
			<title>Displaying .Net Build Warnings in TeamCity</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/displaying-net-build-warnings-in</link>
			<pubDate>Tue, 15 Jan 2013 13:41:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Application Lifecycle Management</category>			<guid isPermaLink="false">2023@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;I like it when I kick off a build and there aren&#039;t any warnings. Unfortunately I&#039;m forgetful and it&#039;s always easier to edit the code now then it is 3 months later (when I remember to look at the warnings). When I put together &lt;a href=&quot;http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project&quot; title=&quot;Wiki writeup on my Continuous Delivery project&quot;&gt;my sample Continuous Delivery project&lt;/a&gt;, I was using Jenkins, which provided plugins for capturing warnings. It was nice to have visual feedback when I added a new warning, see how many were outstanding, have a list of outstanding warnings available on demand, and when I had a few minutes and fixed some of them, positive feedback by watching the warning chart slowly go down.&lt;/p&gt;

&lt;p&gt;When I switch modes and work in &lt;a href=&quot;http://www.jetbrains.com/teamcity/&quot; title=&quot;TeamCity by JetBrains&quot;&gt;TeamCity&lt;/a&gt;, I miss having that information available, with no extra steps from me. Despite several searches, though, I was never able to find a plugin that duplicated that behavior I liked in the Jenkins plugin. Turns out that TeamCity makes it pretty easy to roll your own, with just a little bit of powershell and some built-in features.&lt;/p&gt;

&lt;p&gt;In this post I am going to cover capturing the warnings from an MSBuild build step, adding that warning count to the main dashboard, adding a statistics chart for the warning count over time, adding a condensed list to the end of the build log, adding the formatted list as a build artifact, and adding a custom report tab to report the warnings for each build. &lt;/p&gt;

&lt;p&gt;Because who doesn&#039;t need five different ways to see their warnings?&lt;/p&gt;

&lt;h2&gt;Capturing the Build Warnings&lt;/h2&gt;
&lt;p&gt;Since I am using MSBuild, the build warnings have a consistent pattern and MSBuild itself has an option to log out to a logger. We can add this attribute in either the build step or the Build Parameters. My preference is using the parameters of the build step in case I have multiple MSBuild calls in the build.&lt;/p&gt;

&lt;p&gt;Parameter to add to MSBuild: &lt;code class=&quot;codespan&quot;&gt;/l:FileLogger,Microsoft.Build.Engine;logfile=%BuildLogFilename%&lt;/code&gt;&lt;/p&gt;


&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/MSBuildParameter.png&quot; alt=&quot;Adding the MS Build Parameter&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Adding the MS Build Parameter
&lt;/div&gt;

&lt;p&gt;Each time MSBuild runs, it will log it&#039;s output to the specified file. We can use powershell to extract the warnings from the output, like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb26744&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Param(&lt;br /&gt;&amp;nbsp; &amp;nbsp; [parameter(Mandatory=$true)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; [alias(&amp;quot;f&amp;quot;)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; $FilePath&lt;br /&gt;)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;$warnings = @(Get-Content -ErrorAction Stop $FilePath | &amp;nbsp; &amp;nbsp; &amp;nbsp; # Get the file content&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Where {$_ -match &#039;^.*warning CS.*$&#039;} | &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# Extract lines that match warnings&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; %{ $_.trim() -replace &amp;quot;^\s*\d+&amp;gt;&amp;quot;,&amp;quot;&amp;quot; &amp;nbsp;} | &amp;nbsp; &amp;nbsp; &amp;nbsp;# Strip out any project number and caret prefixes&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sort-object | Get-Unique -asString) &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # remove duplicates by sorting and filtering for unique strings&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb49805&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once we have the warnings extracted, we can move on to decide how we want them delivered. &lt;/p&gt;

&lt;p&gt;&lt;i&gt;Each section below will continue to add on to this script until it contains all the pieces we need to meet the display goals at the beginning.&lt;/i&gt;&lt;/p&gt;

&lt;h2&gt;Condensed Warning List in Build Log&lt;/h2&gt;
&lt;p&gt;The powershell script that is extracting warnings will need to run as a build step in the appropriate build configuration. This means that displaying a formatted list of warnings at the end of the build log is as simple as outputting that list from the powershell script we are building.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb93272&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$count = $warnings.Count&lt;br /&gt;Write-Host &amp;quot;MSBuild Warnings - $count warnings ===================================================&amp;quot;&lt;br /&gt;$warnings | % { Write-Host &amp;quot; * $_&amp;quot; }&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb47823&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will output a section at the bottom of the build log that contains our warnings, like so:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/LogOutput.png&quot; alt=&quot;Warnings in the Bottom of a Build Log&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Warnings in the Bottom of a Build Log
&lt;/div&gt;

&lt;p&gt;Which I suppose is fine, but doesn&#039;t really add that much value over the ones listed further up the log by MSBuild itself.&lt;/p&gt;

&lt;h2&gt;Condensed Warning List in Archived Text File&lt;/h2&gt;
&lt;p&gt;Now that I have formatted warnings, it&#039;s pretty easy to create a file with those warnings and archive it. First I&#039;ll update the script to take an output parameter and add some file output:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb34858&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Param(&lt;br /&gt;&amp;nbsp; &amp;nbsp; [parameter(Mandatory=$true)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; [alias(&amp;quot;f&amp;quot;)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; $FilePath,&lt;br /&gt;&amp;nbsp; &amp;nbsp; [parameter()]&lt;br /&gt;&amp;nbsp; &amp;nbsp; [alias(&amp;quot;o&amp;quot;)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; $RawOutputPath,&lt;br /&gt;)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;# ...&lt;br /&gt;&amp;nbsp;&lt;br /&gt;# file output&lt;br /&gt;if($RawOutputPath){&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream = [System.IO.StreamWriter] $RawOutputPath&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;Build Warnings&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;====================================&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $warnings | % { $stream.WriteLine(&amp;quot; * $_&amp;quot;) }&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.Close()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb77757&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I&#039;ll configure the project to capture that output file as an artifact:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/ArtifactConfig_RawOutput.png&quot; alt=&quot;Artifact Configuration&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Artifact Configuration
&lt;/div&gt;

&lt;p&gt;Et voila, the file shows up in my archived items:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/Artifact_Display.png&quot; alt=&quot;List of archived items from a run&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
List of archived items from a run
&lt;/div&gt;

&lt;p&gt;And I have a clean, archived list of my warnings:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/Artifact_File.png&quot; alt=&quot;Display of archived text file&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Display of archived text file
&lt;/div&gt;

&lt;p&gt;But, really, we can do better.&lt;/p&gt;

&lt;h2&gt;Warning count in build status&lt;/h2&gt;
&lt;p&gt;Part of the goal was to be able to see the warning count change with no extra work, the best place I can think of to meet this is the final build status on each build.&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/BuildStatusBefore.png&quot; alt=&quot;Build status on dashboard&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Build status on dashboard
&lt;/div&gt;

&lt;p&gt;TeamCity provides support for &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatus&quot; title=&quot;TeamCity documentation for Build Script Interaction&quot;&gt;setting the build status from a build script&lt;/a&gt;. By adding some output to the powershell script, like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb95240&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;#TeamCity output&lt;br /&gt;Write-Host &amp;quot;##teamcity[buildStatus text=&#039;{build.status.text}, Build warnings: $count&#039;]&amp;quot;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb45871&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each successful build will also display the number of warnings that were captured.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/BuildStatusAfter.png&quot; alt=&quot;Build status on dashboard, with warnings&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Build status on dashboard, with warnings
&lt;/div&gt;

&lt;p&gt;Better, but what about historical values? And I still don&#039;t like that text file artifact.&lt;/p&gt;

&lt;h2&gt;Warning Count as a Custom Chart&lt;/h2&gt;
&lt;p&gt;TeamCity also provides the ability to add custom charts based on either built-in or &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatistics&quot; title=&quot;TeamCity documentation - reporting custom statistics&quot;&gt;custom statistics&lt;/a&gt;. Custom statistics are reported similar to the build status output above:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb6924&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Write-Host &amp;quot;##teamcity[buildStatisticValue key=&#039;buildWarnings&#039; value=&#039;$count&#039;]&amp;quot;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb74027&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Adding a &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Custom+Chart&quot; title=&quot;TeamCity documentation - Custom Statistics Charts&quot;&gt;custom chart&lt;/a&gt; requires us to dig into the configurations of TeamCity. I&#039;m going to add a chart that will be displayed for any build that provides the warning count number above, so I&#039;ll open the &lt;code class=&quot;codespan&quot;&gt;[teamCity data dir]/config/main-config.xml&lt;/code&gt; file and add the following section:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb12402&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;lt;graph title=&amp;quot;Build Warnings&amp;quot; hideFilters=&amp;quot;showFailed&amp;quot; seriesTitle=&amp;quot;Warning&amp;quot; format=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;valueType key=&amp;quot;buildWarnings&amp;quot; title=&amp;quot;Warnings&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/graph&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb35915&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will add a chart to the Statistics tab of the build. After a few builds this is what I have:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/WarningChart.png&quot; alt=&quot;Build Warning Statistics&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Build Warning Statistics
&lt;/div&gt;

&lt;p&gt;It probably would look better if I hadn&#039;t built with the same number of warnings each time, but you get the point. The mouse hover works just like the built-in charts, linking to the run status for the individual point.&lt;/p&gt;

&lt;p&gt;Ok, getting better, but I think we can take it one step further. &lt;/p&gt;

&lt;h2&gt;Adding a Custom Build Warnings Tab&lt;/h2&gt;
&lt;p&gt;So far we have improved methods of seeing the warning count and watching how it changes over time, but the actual list still leaves something to be desired. Luckily, TeamCity supports &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD3/Including+Third-Party+Reports+in+the+Build+Results#IncludingThird-PartyReportsintheBuildResults-Tabs&quot; title=&quot;TeamCity documentation: Including third-party reports as the build-results tabs&quot;&gt;custom report tabs&lt;/a&gt; in the Build Results. This gives us an easily accessible place to put the warnings and, since it uses HTML, better formatting options than the text file.&lt;/p&gt;

&lt;p&gt;First I need to update the powershell script to output the HTML file. TeamCity will be picking up an entire folder for the report tab, so I could add some external CSS or image files for my report, but I&#039;ll leave that for another day.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb10702&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;# html report output&lt;br /&gt;$check = Test-Path -PathType Container BuildWarningReport&lt;br /&gt;if($check -eq $false){&lt;br /&gt;&amp;nbsp; &amp;nbsp; New-Item &#039;BuildWarningReport&#039; -type Directory&lt;br /&gt;}&lt;br /&gt;$stream = [System.IO.StreamWriter] &amp;quot;BuildWarningReport/index.html&amp;quot;&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;$count Build Warnings&amp;lt;/h1&amp;gt;&amp;quot;)&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;ul&amp;gt;&amp;quot;)&lt;br /&gt;$warnings | % { $stream.WriteLine(&amp;quot;&amp;lt;li&amp;gt;$_&amp;lt;/li&amp;gt;&amp;quot;) }&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;)&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;$stream.Close()&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb41891&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I&#039;ve added HTML output to the script with a hardcoded output location that ensures the report directory exists before writing the index.html page. I&#039;ve hardcoded this value to reduce the amount of thinking &#039;ll need to do as I add this to other projects (keeps it consistent from output name to artifact setting to report tab configuration).&lt;/p&gt;

&lt;p&gt;The next step is to configure the project to capture the folder as an artifact:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/ArtifactConfig_Report.png&quot; alt=&quot;Artifact configuration&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Artifact configuration
&lt;/div&gt;

&lt;p&gt;Then the last step is to modify the TeamCity configuration to recognize that when I output archives like that, I want to treat them as a report. To do this I add the following chunk of XML to my &lt;code class=&quot;codespan&quot;&gt;[TeamCity data directory]/config/main-config.xml&lt;/code&gt; file (per the documentation link above):&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb47391&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;report-tab&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Build Warnings&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;basePath&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;BuildWarningReport&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;startPage&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb11992&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And there we go, the custom report tab is available in the build results:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666; margin: .5em&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/TeamCityBuildWarnings/WarningsTab.png&quot; alt=&quot;Build Warnings tab in Run Results&quot; style=&quot;border: 1px solid #666666;&quot; /&gt;&lt;br /&gt;
Build Warnings tab in Run Results
&lt;/div&gt;

&lt;p&gt;Which takes us from no visibility into our warnings, to five different methods of viewing the information.&lt;/p&gt;

&lt;h2&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;From having to Ctrl+F through the build log all the way to plugin-level output in a few easy steps. After setting this up one time, the only pieces that needed to be repeated for additional builds are the addition of the /logger parameter for MSBuild and the powershell build step to extract the results, and capturing the artifact for the HTML page. All of the output is either built in to the script or applies to the whole server and is displayed whenever the statistics or archive are present in a build.&lt;/p&gt;

&lt;p&gt;Here is the finished script:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb20504&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;Param(&lt;br /&gt;&amp;nbsp; &amp;nbsp; [parameter(Mandatory=$true)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; [alias(&amp;quot;f&amp;quot;)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; $FilePath,&lt;br /&gt;&amp;nbsp; &amp;nbsp; [parameter()]&lt;br /&gt;&amp;nbsp; &amp;nbsp; [alias(&amp;quot;o&amp;quot;)]&lt;br /&gt;&amp;nbsp; &amp;nbsp; $RawOutputPath&lt;br /&gt;)&lt;br /&gt;&amp;nbsp;&lt;br /&gt;$warnings = @(Get-Content -ErrorAction Stop $FilePath | &amp;nbsp; &amp;nbsp; &amp;nbsp; # Get the file content&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Where {$_ -match &#039;^.*warning CS.*$&#039;} | &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# Extract lines that match warnings&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; %{ $_.trim() -replace &amp;quot;^\s*\d+&amp;gt;&amp;quot;,&amp;quot;&amp;quot; &amp;nbsp;} | &amp;nbsp; &amp;nbsp; &amp;nbsp;# Strip out any project number and caret prefixes&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sort-object | Get-Unique -asString) &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # remove duplicates by sorting and filtering for unique strings&lt;br /&gt;&amp;nbsp;&lt;br /&gt;$count = $warnings.Count&lt;br /&gt;&amp;nbsp;&lt;br /&gt;# raw output&lt;br /&gt;Write-Host &amp;quot;MSBuild Warnings - $count warnings ===================================================&amp;quot;&lt;br /&gt;$warnings | % { Write-Host &amp;quot; * $_&amp;quot; }&lt;br /&gt;&amp;nbsp;&lt;br /&gt;#TeamCity output&lt;br /&gt;Write-Host &amp;quot;##teamcity[buildStatus text=&#039;{build.status.text}, Build warnings: $count&#039;]&amp;quot;&lt;br /&gt;Write-Host &amp;quot;##teamcity[buildStatisticValue key=&#039;buildWarnings&#039; value=&#039;$count&#039;]&amp;quot;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;# file output&lt;br /&gt;if($RawOutputPath){&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream = [System.IO.StreamWriter] $RawOutputPath&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;Build Warnings&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;====================================&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.WriteLine(&amp;quot;&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; $warnings | % { $stream.WriteLine(&amp;quot; * $_&amp;quot;) }&lt;br /&gt;&amp;nbsp; &amp;nbsp; $stream.Close()&lt;br /&gt;}&lt;br /&gt;&amp;nbsp;&lt;br /&gt;# html report output&lt;br /&gt;$check = Test-Path -PathType Container BuildWarningReport&lt;br /&gt;if($check -eq $false){&lt;br /&gt;&amp;nbsp; &amp;nbsp; New-Item &#039;BuildWarningReport&#039; -type Directory&lt;br /&gt;}&lt;br /&gt;$stream = [System.IO.StreamWriter] &amp;quot;BuildWarningReport/index.html&amp;quot;&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;$count Build Warnings&amp;lt;/h1&amp;gt;&amp;quot;)&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;ul&amp;gt;&amp;quot;)&lt;br /&gt;$warnings | % { $stream.WriteLine(&amp;quot;&amp;lt;li&amp;gt;$_&amp;lt;/li&amp;gt;&amp;quot;) }&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;)&lt;br /&gt;$stream.WriteLine(&amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;)&lt;br /&gt;$stream.Close()&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb66287&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To recap, we started with some warning messages randomly scattered across the build log. We ended with the warning count automatically showing in the build status on the dashboard, a nice chart of the number over time, and three different ways to view the detailed list. I hope this proves useful to others as well, now I have to go and fix the sample warnings I added before I forget about them. &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/displaying-net-build-warnings-in&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>I like it when I kick off a build and there aren't any warnings. Unfortunately I'm forgetful and it's always easier to edit the code now then it is 3 months later (when I remember to look at the warnings). When I put together <a href="http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project" title="Wiki writeup on my Continuous Delivery project">my sample Continuous Delivery project</a>, I was using Jenkins, which provided plugins for capturing warnings. It was nice to have visual feedback when I added a new warning, see how many were outstanding, have a list of outstanding warnings available on demand, and when I had a few minutes and fixed some of them, positive feedback by watching the warning chart slowly go down.</p>

<p>When I switch modes and work in <a href="http://www.jetbrains.com/teamcity/" title="TeamCity by JetBrains">TeamCity</a>, I miss having that information available, with no extra steps from me. Despite several searches, though, I was never able to find a plugin that duplicated that behavior I liked in the Jenkins plugin. Turns out that TeamCity makes it pretty easy to roll your own, with just a little bit of powershell and some built-in features.</p>

<p>In this post I am going to cover capturing the warnings from an MSBuild build step, adding that warning count to the main dashboard, adding a statistics chart for the warning count over time, adding a condensed list to the end of the build log, adding the formatted list as a build artifact, and adding a custom report tab to report the warnings for each build. </p>

<p>Because who doesn't need five different ways to see their warnings?</p>

<h2>Capturing the Build Warnings</h2>
<p>Since I am using MSBuild, the build warnings have a consistent pattern and MSBuild itself has an option to log out to a logger. We can add this attribute in either the build step or the Build Parameters. My preference is using the parameters of the build step in case I have multiple MSBuild calls in the build.</p>

<p>Parameter to add to MSBuild: <code class="codespan">/l:FileLogger,Microsoft.Build.Engine;logfile=%BuildLogFilename%</code></p>


<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/MSBuildParameter.png" alt="Adding the MS Build Parameter" style="border: 1px solid #666666;" /><br />
Adding the MS Build Parameter
</div>

<p>Each time MSBuild runs, it will log it's output to the specified file. We can use powershell to extract the warnings from the output, like so:</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb67078'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb67078','cb41246'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb67078" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Param(</li><li style="" class="li2">&nbsp; &nbsp; [parameter(Mandatory=$true)]</li><li style="" class="li1">&nbsp; &nbsp; [alias(&quot;f&quot;)]</li><li style="" class="li2">&nbsp; &nbsp; $FilePath</li><li style="" class="li1">)</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">$warnings = @(Get-Content -ErrorAction Stop $FilePath | &nbsp; &nbsp; &nbsp; # Get the file content</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Where {$_ -match '^.*warning CS.*$'} | &nbsp; &nbsp; &nbsp; &nbsp;# Extract lines that match warnings</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; %{ $_.trim() -replace &quot;^\s*\d+&gt;&quot;,&quot;&quot; &nbsp;} | &nbsp; &nbsp; &nbsp;# Strip out any project number and caret prefixes</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sort-object | Get-Unique -asString) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # remove duplicates by sorting and filtering for unique strings</li></ol></div><div id="cb41246" style="display: none; color: red;"></div></div></div>

<p>Once we have the warnings extracted, we can move on to decide how we want them delivered. </p>

<p><i>Each section below will continue to add on to this script until it contains all the pieces we need to meet the display goals at the beginning.</i></p>

<h2>Condensed Warning List in Build Log</h2>
<p>The powershell script that is extracting warnings will need to run as a build step in the appropriate build configuration. This means that displaying a formatted list of warnings at the end of the build log is as simple as outputting that list from the powershell script we are building.</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb20903'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb20903','cb2321'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb20903" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$count = $warnings.Count</li><li style="" class="li2">Write-Host &quot;MSBuild Warnings - $count warnings ===================================================&quot;</li><li style="" class="li1">$warnings | % { Write-Host &quot; * $_&quot; }</li></ol></div><div id="cb2321" style="display: none; color: red;"></div></div></div>

<p>This will output a section at the bottom of the build log that contains our warnings, like so:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/LogOutput.png" alt="Warnings in the Bottom of a Build Log" style="border: 1px solid #666666;" /><br />
Warnings in the Bottom of a Build Log
</div>

<p>Which I suppose is fine, but doesn't really add that much value over the ones listed further up the log by MSBuild itself.</p>

<h2>Condensed Warning List in Archived Text File</h2>
<p>Now that I have formatted warnings, it's pretty easy to create a file with those warnings and archive it. First I'll update the script to take an output parameter and add some file output:</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb32223'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb32223','cb54419'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb32223" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Param(</li><li style="" class="li2">&nbsp; &nbsp; [parameter(Mandatory=$true)]</li><li style="" class="li1">&nbsp; &nbsp; [alias(&quot;f&quot;)]</li><li style="" class="li2">&nbsp; &nbsp; $FilePath,</li><li style="" class="li1">&nbsp; &nbsp; [parameter()]</li><li style="" class="li2">&nbsp; &nbsp; [alias(&quot;o&quot;)]</li><li style="" class="li1">&nbsp; &nbsp; $RawOutputPath,</li><li style="" class="li2">)</li><li style="" class="li1">&nbsp;</li><li style="" class="li2"># ...</li><li style="" class="li1">&nbsp;</li><li style="" class="li2"># file output</li><li style="" class="li1">if($RawOutputPath){</li><li style="" class="li2">&nbsp; &nbsp; $stream = [System.IO.StreamWriter] $RawOutputPath</li><li style="" class="li1">&nbsp; &nbsp; $stream.WriteLine(&quot;Build Warnings&quot;)</li><li style="" class="li2">&nbsp; &nbsp; $stream.WriteLine(&quot;====================================&quot;)</li><li style="" class="li1">&nbsp; &nbsp; $stream.WriteLine(&quot;&quot;)</li><li style="" class="li2">&nbsp; &nbsp; $warnings | % { $stream.WriteLine(&quot; * $_&quot;) }</li><li style="" class="li1">&nbsp; &nbsp; $stream.Close()</li><li style="" class="li2">}</li></ol></div><div id="cb54419" style="display: none; color: red;"></div></div></div>

<p>Then I'll configure the project to capture that output file as an artifact:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/ArtifactConfig_RawOutput.png" alt="Artifact Configuration" style="border: 1px solid #666666;" /><br />
Artifact Configuration
</div>

<p>Et voila, the file shows up in my archived items:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/Artifact_Display.png" alt="List of archived items from a run" style="border: 1px solid #666666;" /><br />
List of archived items from a run
</div>

<p>And I have a clean, archived list of my warnings:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/Artifact_File.png" alt="Display of archived text file" style="border: 1px solid #666666;" /><br />
Display of archived text file
</div>

<p>But, really, we can do better.</p>

<h2>Warning count in build status</h2>
<p>Part of the goal was to be able to see the warning count change with no extra work, the best place I can think of to meet this is the final build status on each build.</p>

<p>Before:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/BuildStatusBefore.png" alt="Build status on dashboard" style="border: 1px solid #666666;" /><br />
Build status on dashboard
</div>

<p>TeamCity provides support for <a href="http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatus" title="TeamCity documentation for Build Script Interaction">setting the build status from a build script</a>. By adding some output to the powershell script, like so:</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb27531'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb27531','cb6255'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb27531" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">#TeamCity output</li><li style="" class="li2">Write-Host &quot;##teamcity[buildStatus text='{build.status.text}, Build warnings: $count']&quot;</li></ol></div><div id="cb6255" style="display: none; color: red;"></div></div></div>

<p>Each successful build will also display the number of warnings that were captured.</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/BuildStatusAfter.png" alt="Build status on dashboard, with warnings" style="border: 1px solid #666666;" /><br />
Build status on dashboard, with warnings
</div>

<p>Better, but what about historical values? And I still don't like that text file artifact.</p>

<h2>Warning Count as a Custom Chart</h2>
<p>TeamCity also provides the ability to add custom charts based on either built-in or <a href="http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatistics" title="TeamCity documentation - reporting custom statistics">custom statistics</a>. Custom statistics are reported similar to the build status output above:</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb22733'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb22733','cb34867'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb22733" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Write-Host &quot;##teamcity[buildStatisticValue key='buildWarnings' value='$count']&quot;</li></ol></div><div id="cb34867" style="display: none; color: red;"></div></div></div>

<p>Adding a <a href="http://confluence.jetbrains.net/display/TCD7/Custom+Chart" title="TeamCity documentation - Custom Statistics Charts">custom chart</a> requires us to dig into the configurations of TeamCity. I'm going to add a chart that will be displayed for any build that provides the warning count number above, so I'll open the <code class="codespan">[teamCity data dir]/config/main-config.xml</code> file and add the following section:</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb27795'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb27795','cb5714'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb27795" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&lt;graph title=&quot;Build Warnings&quot; hideFilters=&quot;showFailed&quot; seriesTitle=&quot;Warning&quot; format=&quot;&quot;&gt;</li><li style="" class="li2">&nbsp; &nbsp; &lt;valueType key=&quot;buildWarnings&quot; title=&quot;Warnings&quot;/&gt;</li><li style="" class="li1">&lt;/graph&gt;</li></ol></div><div id="cb5714" style="display: none; color: red;"></div></div></div>

<p>This will add a chart to the Statistics tab of the build. After a few builds this is what I have:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/WarningChart.png" alt="Build Warning Statistics" style="border: 1px solid #666666;" /><br />
Build Warning Statistics
</div>

<p>It probably would look better if I hadn't built with the same number of warnings each time, but you get the point. The mouse hover works just like the built-in charts, linking to the run status for the individual point.</p>

<p>Ok, getting better, but I think we can take it one step further. </p>

<h2>Adding a Custom Build Warnings Tab</h2>
<p>So far we have improved methods of seeing the warning count and watching how it changes over time, but the actual list still leaves something to be desired. Luckily, TeamCity supports <a href="http://confluence.jetbrains.net/display/TCD3/Including+Third-Party+Reports+in+the+Build+Results#IncludingThird-PartyReportsintheBuildResults-Tabs" title="TeamCity documentation: Including third-party reports as the build-results tabs">custom report tabs</a> in the Build Results. This gives us an easily accessible place to put the warnings and, since it uses HTML, better formatting options than the text file.</p>

<p>First I need to update the powershell script to output the HTML file. TeamCity will be picking up an entire folder for the report tab, so I could add some external CSS or image files for my report, but I'll leave that for another day.</p>

<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb23304'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb23304','cb4114'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb23304" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"># html report output</li><li style="" class="li2">$check = Test-Path -PathType Container BuildWarningReport</li><li style="" class="li1">if($check -eq $false){</li><li style="" class="li2">&nbsp; &nbsp; New-Item 'BuildWarningReport' -type Directory</li><li style="" class="li1">}</li><li style="" class="li2">$stream = [System.IO.StreamWriter] &quot;BuildWarningReport/index.html&quot;</li><li style="" class="li1">$stream.WriteLine(&quot;&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;$count Build Warnings&lt;/h1&gt;&quot;)</li><li style="" class="li2">$stream.WriteLine(&quot;&lt;ul&gt;&quot;)</li><li style="" class="li1">$warnings | % { $stream.WriteLine(&quot;&lt;li&gt;$_&lt;/li&gt;&quot;) }</li><li style="" class="li2">$stream.WriteLine(&quot;&lt;/ul&gt;&quot;)</li><li style="" class="li1">$stream.WriteLine(&quot;&lt;/body&gt;&lt;/html&gt;&quot;)</li><li style="" class="li2">$stream.Close()</li></ol></div><div id="cb4114" style="display: none; color: red;"></div></div></div>

<p>I've added HTML output to the script with a hardcoded output location that ensures the report directory exists before writing the index.html page. I've hardcoded this value to reduce the amount of thinking 'll need to do as I add this to other projects (keeps it consistent from output name to artifact setting to report tab configuration).</p>

<p>The next step is to configure the project to capture the folder as an artifact:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/ArtifactConfig_Report.png" alt="Artifact configuration" style="border: 1px solid #666666;" /><br />
Artifact configuration
</div>

<p>Then the last step is to modify the TeamCity configuration to recognize that when I output archives like that, I want to treat them as a report. To do this I add the following chunk of XML to my <code class="codespan">[TeamCity data directory]/config/main-config.xml</code> file (per the documentation link above):</p>

<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb46329'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb46329','cb74417'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb46329" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;report-tab</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Build Warnings&quot;</span> <span style="color: #000066;">basePath</span>=<span style="color: #ff0000;">&quot;BuildWarningReport&quot;</span> <span style="color: #000066;">startPage</span>=<span style="color: #ff0000;">&quot;index.html&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li></ol></div><div id="cb74417" style="display: none; color: red;"></div></div></div>

<p>And there we go, the custom report tab is available in the build results:</p>

<div style="text-align: center; font-size: 90%; color: #666666; margin: .5em">
<img src="http://tiernok.com/LTDBlog/TeamCityBuildWarnings/WarningsTab.png" alt="Build Warnings tab in Run Results" style="border: 1px solid #666666;" /><br />
Build Warnings tab in Run Results
</div>

<p>Which takes us from no visibility into our warnings, to five different methods of viewing the information.</p>

<h2>Wrap-up</h2>
<p>From having to Ctrl+F through the build log all the way to plugin-level output in a few easy steps. After setting this up one time, the only pieces that needed to be repeated for additional builds are the addition of the /logger parameter for MSBuild and the powershell build step to extract the results, and capturing the artifact for the HTML page. All of the output is either built in to the script or applies to the whole server and is displayed whenever the statistics or archive are present in a build.</p>

<p>Here is the finished script:</p>
<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb82208'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb82208','cb42017'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb82208" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">Param(</li><li style="" class="li2">&nbsp; &nbsp; [parameter(Mandatory=$true)]</li><li style="" class="li1">&nbsp; &nbsp; [alias(&quot;f&quot;)]</li><li style="" class="li2">&nbsp; &nbsp; $FilePath,</li><li style="" class="li1">&nbsp; &nbsp; [parameter()]</li><li style="" class="li2">&nbsp; &nbsp; [alias(&quot;o&quot;)]</li><li style="" class="li1">&nbsp; &nbsp; $RawOutputPath</li><li style="" class="li2">)</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">$warnings = @(Get-Content -ErrorAction Stop $FilePath | &nbsp; &nbsp; &nbsp; # Get the file content</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Where {$_ -match '^.*warning CS.*$'} | &nbsp; &nbsp; &nbsp; &nbsp;# Extract lines that match warnings</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; %{ $_.trim() -replace &quot;^\s*\d+&gt;&quot;,&quot;&quot; &nbsp;} | &nbsp; &nbsp; &nbsp;# Strip out any project number and caret prefixes</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sort-object | Get-Unique -asString) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # remove duplicates by sorting and filtering for unique strings</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">$count = $warnings.Count</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"># raw output</li><li style="" class="li2">Write-Host &quot;MSBuild Warnings - $count warnings ===================================================&quot;</li><li style="" class="li1">$warnings | % { Write-Host &quot; * $_&quot; }</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">#TeamCity output</li><li style="" class="li2">Write-Host &quot;##teamcity[buildStatus text='{build.status.text}, Build warnings: $count']&quot;</li><li style="" class="li1">Write-Host &quot;##teamcity[buildStatisticValue key='buildWarnings' value='$count']&quot;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"># file output</li><li style="" class="li2">if($RawOutputPath){</li><li style="" class="li1">&nbsp; &nbsp; $stream = [System.IO.StreamWriter] $RawOutputPath</li><li style="" class="li2">&nbsp; &nbsp; $stream.WriteLine(&quot;Build Warnings&quot;)</li><li style="" class="li1">&nbsp; &nbsp; $stream.WriteLine(&quot;====================================&quot;)</li><li style="" class="li2">&nbsp; &nbsp; $stream.WriteLine(&quot;&quot;)</li><li style="" class="li1">&nbsp; &nbsp; $warnings | % { $stream.WriteLine(&quot; * $_&quot;) }</li><li style="" class="li2">&nbsp; &nbsp; $stream.Close()</li><li style="" class="li1">}</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"># html report output</li><li style="" class="li2">$check = Test-Path -PathType Container BuildWarningReport</li><li style="" class="li1">if($check -eq $false){</li><li style="" class="li2">&nbsp; &nbsp; New-Item 'BuildWarningReport' -type Directory</li><li style="" class="li1">}</li><li style="" class="li2">$stream = [System.IO.StreamWriter] &quot;BuildWarningReport/index.html&quot;</li><li style="" class="li1">$stream.WriteLine(&quot;&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;$count Build Warnings&lt;/h1&gt;&quot;)</li><li style="" class="li2">$stream.WriteLine(&quot;&lt;ul&gt;&quot;)</li><li style="" class="li1">$warnings | % { $stream.WriteLine(&quot;&lt;li&gt;$_&lt;/li&gt;&quot;) }</li><li style="" class="li2">$stream.WriteLine(&quot;&lt;/ul&gt;&quot;)</li><li style="" class="li1">$stream.WriteLine(&quot;&lt;/body&gt;&lt;/html&gt;&quot;)</li><li style="" class="li2">$stream.Close()</li></ol></div><div id="cb42017" style="display: none; color: red;"></div></div></div>

<p>To recap, we started with some warning messages randomly scattered across the build log. We ended with the warning count automatically showing in the build status on the dashboard, a nice chart of the number over time, and three different ways to view the detailed list. I hope this proves useful to others as well, now I have to go and fix the sample warnings I added before I forget about them. <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/displaying-net-build-warnings-in">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/displaying-net-build-warnings-in#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=2023</wfw:commentRss>
		</item>
				<item>
			<title>Custom Charts in TeamCity</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/custom-charts-in-teamcity</link>
			<pubDate>Fri, 21 Sep 2012 11:23:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Application Lifecycle Management</category>			<guid isPermaLink="false">1836@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;While re-implementing my continuous delivery pipeline in TeamCity &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity&quot; title=&quot;Continuous Delivery with TeamCity&quot; target=&quot;_blank&quot;&gt;last week&lt;/a&gt;, I skipped over charting the load tests results from WCAT. This weekend I returned to this task to find that it was less tricky than it originally appeared.&lt;/p&gt;

&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;TeamCity has project and build level custom charting that is driven by settings files. These files and the format of the necessary chart definition are &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Custom+Chart&quot; title=&quot;Custom Charts on TeamCity 7&quot; target=&quot;_blank&quot;&gt;documented&lt;/a&gt;. The actual graph XML is pretty straightforward and easy to implement. The files refer to values that are provided by TeamCity (listed on that page), via a special file you drop during your build (&lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-provideStatsUsingFile&quot; title=&quot;teamcity-info.xml Details&quot; target=&quot;_blank&quot;&gt;teamcity-info.xml&lt;/a&gt;), or via &lt;a href=&quot;http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatistics&quot; title=&quot;Service Message Details&quot; target=&quot;_blank&quot;&gt;service messages&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Graph Definitions&lt;/h2&gt;
&lt;p&gt;In the Jenkins build I had graphed the following data from the WCAT run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate Graph - Transactions Per Second, Requests Per Second&lt;/li&gt;
&lt;li&gt;Totals Graph - Total Transaction count, Total Request count, Total Error count&lt;/li&gt;
&lt;li&gt;Response Time to Last Byte Graph - Minimum, Average, 95th Percentile, 99th Percentile, Maximum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To match this in TeamCity I created the project-wide graph settings like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb32339&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;?xml&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;version&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;encoding&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;settings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;custom-graphs&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;graph&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Rate&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;hideFilters&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;showFailed&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;seriesTitle&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;some key&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;format&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TransactionsPerSecond&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Transactions/s&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;RequestsPerSecond&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Requests/s&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/graph&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;graph&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Totals&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;hideFilters&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;showFailed&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;seriesTitle&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;some key&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;format&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalTransactions&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Total Transactions&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalRequests&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Total Requests&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalErrors&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Total Errors&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/graph&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;graph&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Response Time (to Last Byte)&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;hideFilters&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;showFailed&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;seriesTitle&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;some key&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;format&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;duration&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeAverage&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Average&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeMinimum&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Minimum&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeNinetyFivePercent&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Ninety Fifth Percent&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeNinetyNinePercent&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Ninety Nineth Percent&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;valueType&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeMaximum&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;title&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Maximum&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;buildTypeId&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;bt4&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/graph&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/custom-graphs&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/settings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb9352&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;


&lt;p&gt;Besides the key in each valueType tag, I also include a buildTypeid that is the internal build ID for my automated load test build. I found this in the address bar after clicking the build&#039;s link on the dashboard:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityCharts_link.png&quot; alt=&quot;TeamCity - Build URL&quot; /&gt;&lt;br /&gt;
	Team City - Build URL
&lt;/div&gt;

&lt;h2&gt;Defining the Data&lt;/h2&gt;
&lt;p&gt;Once I have the definitions roughly the way I want them, I need to make the data available from the WCAT run. WCAT already produces it&#039;s results in XML and for Jenkins I created an XSL to transform the results into a new XML file that would be easier to plot in Jenkins. For TeamCity I&#039;ll do the same, but this time I decided to use powershell to execute the transformation.&lt;/p&gt;

&lt;p&gt;The WCAT results take some work to decode, as they are stored in XML that is halfway to presentation format. Luckily I have already done this once, so creating a new XSL is fairly easy:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb63371&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:stylesheet&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;xmlns:xsl&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;version&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;match&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;build&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TransactionsPerSecond&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;summary&amp;quot;]/table[@name=&amp;quot;summarydata&amp;quot;]/item/data[@name=&amp;quot;tps&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;RequestsPerSecond&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;summary&amp;quot;]/table[@name=&amp;quot;summarydata&amp;quot;]/item/data[@name=&amp;quot;rps&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalTransactions&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;requeststats&amp;quot;]/item[1]/data[@name=&amp;quot;transactions&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalRequests&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;requeststats&amp;quot;]/item[1]/data[@name=&amp;quot;requests&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;TotalErrors&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;summary&amp;quot;]/table[@name=&amp;quot;summarydata&amp;quot;]/item/data[@name=&amp;quot;terrors&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeAverage&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;histogram&amp;quot;]/item[2]/data[@name=&amp;quot;response_time_avg&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeMinimum&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;histogram&amp;quot;]/item[2]/data[@name=&amp;quot;response_time_min&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeNinetyFivePercent&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;histogram&amp;quot;]/item[2]/data[@name=&amp;quot;response_time_95&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeNinetyNinePercent&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;histogram&amp;quot;]/item[2]/data[@name=&amp;quot;response_time_99&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;statisticValue&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;ResponseTimeMaximum&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:attribute&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;select&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&#039;//section[@name=&amp;quot;details&amp;quot;]/table[@name=&amp;quot;histogram&amp;quot;]/item[2]/data[@name=&amp;quot;response_time_max&amp;quot;]&#039;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:attribute&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/statisticValue&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/build&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:template&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/xsl:stylesheet&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb95988&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(The final XSL is located on &lt;a href=&quot;https://bitbucket.org/tarwn/mvcmusicstore.loadtest/src/ee1713fd00ff/TransformForTeamCity.xsl&quot; title=&quot;TransformForTeamCity.xsl from tarwn / MVCMusicStore.LoadTest&quot;&gt;bitbucket&lt;/a&gt; in the event that I make any future changes).&lt;/p&gt;

&lt;p&gt;The powershell command to run the transform is then as easy as:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;powershell&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;powershell&quot; id=&quot;cb63072&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;$xsl = (New-Object System.Xml.Xsl.XslCompiledTransform)&lt;br /&gt;$xsl.Load(&#039;%system.teamcity.build.workingDir%/TransformForTeamCity.xsl&#039;)&lt;br /&gt;$xsl.Transform(&#039;%system.teamcity.build.workingDir%/log.xml&#039;,&#039;%system.teamcity.build.workingDir%/teamcity-info.xml&#039;)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb52951&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;TeamCity replaces the variables with the correct values and transforms the generated log into the specially named &quot;teamcity-info.xml&quot; file.&lt;/p&gt;

&lt;h2&gt;Done&lt;/h2&gt;
&lt;p&gt;I wasn&#039;t happy about editing settings file in TeamCity directly, especially given the implications this has in a real environment (how many people would need to be involved to do this in a heavily audited environment?). However that was the only issue I had and I am happy with the results (the build versions are the same because I rebuilt several times so I would have a decent chart):&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBLog/ContinuousDelivery/TeamCityCharts.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityCharts_sm.png&quot; alt=&quot;TeamCity - Finished Charts&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Finished Charts
&lt;/div&gt;

&lt;p&gt;For bonus points, the same functionality you have with the built-in charts is available here. You can change the time period, view averages of the values, and change which points are graphed by checking or unchecking them.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/custom-charts-in-teamcity&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>While re-implementing my continuous delivery pipeline in TeamCity <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity" title="Continuous Delivery with TeamCity" target="_blank">last week</a>, I skipped over charting the load tests results from WCAT. This weekend I returned to this task to find that it was less tricky than it originally appeared.</p>

<h2>How it works</h2>
<p>TeamCity has project and build level custom charting that is driven by settings files. These files and the format of the necessary chart definition are <a href="http://confluence.jetbrains.net/display/TCD7/Custom+Chart" title="Custom Charts on TeamCity 7" target="_blank">documented</a>. The actual graph XML is pretty straightforward and easy to implement. The files refer to values that are provided by TeamCity (listed on that page), via a special file you drop during your build (<a href="http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-provideStatsUsingFile" title="teamcity-info.xml Details" target="_blank">teamcity-info.xml</a>), or via <a href="http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildStatistics" title="Service Message Details" target="_blank">service messages</a>.</p>

<h2>Graph Definitions</h2>
<p>In the Jenkins build I had graphed the following data from the WCAT run:</p>

<ul>
<li>Rate Graph - Transactions Per Second, Requests Per Second</li>
<li>Totals Graph - Total Transaction count, Total Request count, Total Error count</li>
<li>Response Time to Last Byte Graph - Minimum, Average, 95th Percentile, 99th Percentile, Maximum</li>
</ul>

<p>To match this in TeamCity I created the project-wide graph settings like so:</p>

<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb12797'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb12797','cb6835'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb12797" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;UTF-8&quot;</span><span style="font-weight: bold; color: black;">?&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;settings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;custom-graphs<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp;<span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;graph</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Rate&quot;</span> <span style="color: #000066;">hideFilters</span>=<span style="color: #ff0000;">&quot;showFailed&quot;</span> <span style="color: #000066;">seriesTitle</span>=<span style="color: #ff0000;">&quot;some key&quot;</span> <span style="color: #000066;">format</span>=<span style="color: #ff0000;">&quot;&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TransactionsPerSecond&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Transactions/s&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;RequestsPerSecond&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Requests/s&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/graph<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp;<span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;graph</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Totals&quot;</span> <span style="color: #000066;">hideFilters</span>=<span style="color: #ff0000;">&quot;showFailed&quot;</span> <span style="color: #000066;">seriesTitle</span>=<span style="color: #ff0000;">&quot;some key&quot;</span> <span style="color: #000066;">format</span>=<span style="color: #ff0000;">&quot;&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalTransactions&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Total Transactions&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalRequests&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Total Requests&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalErrors&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Total Errors&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/graph<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp;<span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;graph</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Response Time (to Last Byte)&quot;</span> <span style="color: #000066;">hideFilters</span>=<span style="color: #ff0000;">&quot;showFailed&quot;</span> <span style="color: #000066;">seriesTitle</span>=<span style="color: #ff0000;">&quot;some key&quot;</span> <span style="color: #000066;">format</span>=<span style="color: #ff0000;">&quot;duration&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeAverage&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Average&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeMinimum&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Minimum&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeNinetyFivePercent&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Ninety Fifth Percent&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeNinetyNinePercent&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Ninety Nineth Percent&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;valueType</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeMaximum&quot;</span> <span style="color: #000066;">title</span>=<span style="color: #ff0000;">&quot;Maximum&quot;</span> <span style="color: #000066;">buildTypeId</span>=<span style="color: #ff0000;">&quot;bt4&quot;</span><span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/graph<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/custom-graphs<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/settings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li></ol></div><div id="cb6835" style="display: none; color: red;"></div></div></div>


<p>Besides the key in each valueType tag, I also include a buildTypeid that is the internal build ID for my automated load test build. I found this in the address bar after clicking the build's link on the dashboard:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityCharts_link.png" alt="TeamCity - Build URL" /><br />
	Team City - Build URL
</div>

<h2>Defining the Data</h2>
<p>Once I have the definitions roughly the way I want them, I need to make the data available from the WCAT run. WCAT already produces it's results in XML and for Jenkins I created an XSL to transform the results into a new XML file that would be easier to plot in Jenkins. For TeamCity I'll do the same, but this time I decided to use powershell to execute the transformation.</p>

<p>The WCAT results take some work to decode, as they are stored in XML that is halfway to presentation format. Luckily I have already done this once, so creating a new XSL is fairly easy:</p>

<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb21535'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb21535','cb57251'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb21535" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:stylesheet</span> <span style="color: #000066;">xmlns:xsl</span>=<span style="color: #ff0000;">&quot;http://www.w3.org/1999/XSL/Transform&quot;</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:template</span> <span style="color: #000066;">match</span>=<span style="color: #ff0000;">&quot;/&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;build<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TransactionsPerSecond&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;summary&quot;]/table[@name=&quot;summarydata&quot;]/item/data[@name=&quot;tps&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;RequestsPerSecond&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;summary&quot;]/table[@name=&quot;summarydata&quot;]/item/data[@name=&quot;rps&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalTransactions&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;requeststats&quot;]/item[1]/data[@name=&quot;transactions&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalRequests&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;requeststats&quot;]/item[1]/data[@name=&quot;requests&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;TotalErrors&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;summary&quot;]/table[@name=&quot;summarydata&quot;]/item/data[@name=&quot;terrors&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeAverage&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;histogram&quot;]/item[2]/data[@name=&quot;response_time_avg&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeMinimum&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;histogram&quot;]/item[2]/data[@name=&quot;response_time_min&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeNinetyFivePercent&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;histogram&quot;]/item[2]/data[@name=&quot;response_time_95&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeNinetyNinePercent&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;histogram&quot;]/item[2]/data[@name=&quot;response_time_99&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;statisticValue</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;ResponseTimeMaximum&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:attribute</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;value&quot;</span><span style="font-weight: bold; color: black;">&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;xsl:value-of</span> <span style="color: #000066;">select</span>=<span style="color: #ff0000;">'//section[@name=&quot;details&quot;]/table[@name=&quot;histogram&quot;]/item[2]/data[@name=&quot;response_time_max&quot;]'</span> <span style="font-weight: bold; color: black;">/&gt;</span></span><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:attribute<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/statisticValue<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/build<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:template<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/xsl:stylesheet<span style="font-weight: bold; color: black;">&gt;</span></span></span></li></ol></div><div id="cb57251" style="display: none; color: red;"></div></div></div>

<p>(The final XSL is located on <a href="https://bitbucket.org/tarwn/mvcmusicstore.loadtest/src/ee1713fd00ff/TransformForTeamCity.xsl" title="TransformForTeamCity.xsl from tarwn / MVCMusicStore.LoadTest">bitbucket</a> in the event that I make any future changes).</p>

<p>The powershell command to run the transform is then as easy as:</p>
<div class="codebox"><div class="codeheader"><span>powershell</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb71059'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb71059','cb29806'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="powershell" id="cb71059" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">$xsl = (New-Object System.Xml.Xsl.XslCompiledTransform)</li><li style="" class="li2">$xsl.Load('%system.teamcity.build.workingDir%/TransformForTeamCity.xsl')</li><li style="" class="li1">$xsl.Transform('%system.teamcity.build.workingDir%/log.xml','%system.teamcity.build.workingDir%/teamcity-info.xml')</li></ol></div><div id="cb29806" style="display: none; color: red;"></div></div></div>

<p>TeamCity replaces the variables with the correct values and transforms the generated log into the specially named "teamcity-info.xml" file.</p>

<h2>Done</h2>
<p>I wasn't happy about editing settings file in TeamCity directly, especially given the implications this has in a real environment (how many people would need to be involved to do this in a heavily audited environment?). However that was the only issue I had and I am happy with the results (the build versions are the same because I rebuilt several times so I would have a decent chart):</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBLog/ContinuousDelivery/TeamCityCharts.png" target="_blank"><br />
		<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityCharts_sm.png" alt="TeamCity - Finished Charts" /><br />
	</a><br />
	Team City - Finished Charts
</div>

<p>For bonus points, the same functionality you have with the built-in charts is available here. You can change the time period, view averages of the values, and change which points are graphed by checking or unchecking them.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/custom-charts-in-teamcity">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/custom-charts-in-teamcity#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1836</wfw:commentRss>
		</item>
				<item>
			<title>Continuous Delivery - To The Cloud!</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-to-the-cloud</link>
			<pubDate>Thu, 20 Sep 2012 10:46:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Application Lifecycle Management</category>			<guid isPermaLink="false">1837@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;So what do you do when you have a nice little build lab with two  parallel automated deployment pipelines that include unit testing, interface testing, deployments, smoke testing, load testing, static analysis, and automated sort-of QA and production deployments? &lt;/p&gt;

&lt;p&gt;Take it to the cloud, of course. &lt;/p&gt;

&lt;p&gt;(Well, and write &lt;a href=&quot;http://blogs.lessthandot.com/index.php/All/continuous+delivery:&quot; title=&quot;Continuous Delivery posts&quot;&gt;a bunch of blog posts&lt;/a&gt; and &lt;a href=&quot;http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project&quot; title=&quot;Continuous Delivery project wiki page&quot;&gt;a wiki page&lt;/a&gt; too)&lt;/p&gt;

&lt;h2&gt;Azure Setup&lt;/h2&gt;
&lt;p&gt;The target of this exercise was to get the project deploying automatically to the cloud, and I wasn&#039;t particular about where. I decided that the &lt;a href=&quot;https://www.windowsazure.com/en-us/home/scenarios/web-sites/&quot; title=&quot;About the Azure Websites Feature&quot;&gt;free website&lt;/a&gt; feature in Azure would be a good target. Mostly due to the free part but also because I suspected it would be easy.&lt;/p&gt;

&lt;p&gt;First up was creating an Azure account, which I did via &lt;a href=&quot;https://www.windowsazure.com/en-us/&quot; title=&quot;WindowsAzure.com&quot;&gt;WindowsAzure.com&lt;/a&gt;. The Web Sites feature is still in Preview mode, so I had to go the preview features and enable it.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-01.png&quot; alt=&quot;WindowsAzure.com - Preview Features Option&quot; /&gt;&lt;br /&gt;
	WindowsAzure.com - Preview Features Option
&lt;/div&gt;

&lt;p&gt;The Web Sites option is at the bottom and enabled with a button and dialog:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-02.png&quot; alt=&quot;WindowsAzure.com - Preview Features&quot; /&gt;&lt;br /&gt;
	WindowsAzure.com - Preview Features
&lt;/div&gt;

&lt;p&gt;Once it&#039;s enabled, I can go into the portal and when I press the &quot;New&quot; button at the bottom, I&#039;ll have options to create a new Web Site instance.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-03.png&quot; alt=&quot;WindowsAzure.com - New Website&quot; /&gt;&lt;br /&gt;
	WindowsAzure.com - New Website
&lt;/div&gt;

&lt;p&gt;After creating the new site, it will provision and show up in my dashboard:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-04.png&quot; alt=&quot;WindowsAzure.com - Dashboard&quot; /&gt;&lt;br /&gt;
	WindowsAzure.com - Dashboard
&lt;/div&gt;

&lt;p&gt;That&#039;s all it takes to have a new Web Site in Windows Azure. Of course it&#039;s not doing anything useful at the moment, so lets go back to the build process and fix that.&lt;/p&gt;

&lt;h2&gt;Build Process Changes&lt;/h2&gt;
&lt;p&gt;Because I chose to use an Azure Web Site, the build changes are going to be minimal. Microsoft intends us to use the Publish feature in Visual Studio or Web Matrix to deploy our websites, so they give us all the settings we need to run the msdeploy executable from a build step instead. &lt;/p&gt;

&lt;p&gt;In the properties dashboard for the new website above, there is a link to download the publish settings for the site and a link to setup credentials. First setup the credentials, then click the download link to download the publish settings. These settings are intended for Visual Studio or Web Matrix, but being XML we can read the file and pluck out the important values.&lt;/p&gt;

&lt;p&gt;I made a copy of my final &quot;Deploy to Production&quot; step and modified the msdeploy step to use a new Param file. The Param file is used during the deployment to set the IIS name and connection strings, you can see all of them on &lt;a href=&quot;https://bitbucket.org/tarwn/mvcmusicstore.main/src/1560a5fd57ea/Configs&quot; title=&quot;Parameter files at tarwn/MVCMusicStore.Main&quot;&gt;BitBucket&lt;/a&gt;. &lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-05.png&quot; alt=&quot;TeamCity Build Steps&quot; /&gt;&lt;br /&gt;
	TeamCity Build Steps
&lt;/div&gt;

&lt;p&gt;&lt;b&gt;MSDeploy Command (TeamCity version):&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb55593&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;quot;C:\Program Files\IIS\Microsoft Web Deploy V2\msdeploy.exe&amp;quot; -source:package=&#039;%system.teamcity.build.workingDir%\PriorArtifacts\MvcMusicStore.zip&#039; -dest:auto,computerName=&#039;%COMPUTER%&#039;,userName=&#039;%USERNAME%&#039;,password=&#039;%PASSWORD%&#039;,includeAcls=&#039;False&#039;,authtype=basic -allowUntrusted &amp;nbsp;-verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile=&amp;quot;%system.teamcity.build.workingDir%\PriorArtifacts\MvcMusicStore.AZUREWEBSITE.xml&amp;quot; -enableRule:DoNotDeleteRule&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb35286&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I then updated the Configuration Parameters to reflect the Azure credentials and URL so the deployment and smoke test would work properly and entered the username and password entered in the Azure Dashboard in the prior step.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-07.png&quot; alt=&quot;TeamCity Configuration Parameters&quot; /&gt;&lt;br /&gt;
	TeamCity Configuration Parameters
&lt;/div&gt;

&lt;p&gt;And that&#039;s it, probably the fastest on premises to cloud change. Press the build button in TeamCity and we have a green build to the cloud:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-08.png&quot; alt=&quot;TeamCity Build Successful&quot; /&gt;&lt;br /&gt;
	TeamCity Build Successful
&lt;/div&gt;

&lt;p&gt;And after a similar set of changes in Jenkins, I have a parallel production step that targets Azure as well:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-9.png&quot; alt=&quot;Jenkins Build Pipeline&quot; /&gt;&lt;br /&gt;
	Jenkins Build Pipeline
&lt;/div&gt;

&lt;p&gt;And of course I had to push the button a few times:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-10.png&quot; alt=&quot;Jenkins Build Success&quot; /&gt;&lt;br /&gt;
	Jenkins Build Success
&lt;/div&gt;

&lt;p&gt;You can see the site here: &lt;a href=&quot;http://elismvcmusicstore.azurewebsites.net&quot;&gt;http://elismvcmusicstore.azurewebsites.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: It will probably load slowly. It&#039;s on a shared node and seems to go to sleep since it gets visited so infrequently. If I felt like paying for it, it&#039;s just a toggle button away from running as a reserved instance (and another to scale it up to multiple instances)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;That was the easy button version&lt;/h2&gt;
&lt;p&gt;Despite the fact that I chose the easiest possible target, as long as we can script the deployment we can easily add it into the pipeline and know that our deployment is taking exactly the same steps every time it deploys. Whether we were instead deploying to an Azure web role, deploying EC2 instances, or pushing to another provider, we have the framework to do so consistently and safely.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-to-the-cloud&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>So what do you do when you have a nice little build lab with two  parallel automated deployment pipelines that include unit testing, interface testing, deployments, smoke testing, load testing, static analysis, and automated sort-of QA and production deployments? </p>

<p>Take it to the cloud, of course. </p>

<p>(Well, and write <a href="http://blogs.lessthandot.com/index.php/All/continuous+delivery:" title="Continuous Delivery posts">a bunch of blog posts</a> and <a href="http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project" title="Continuous Delivery project wiki page">a wiki page</a> too)</p>

<h2>Azure Setup</h2>
<p>The target of this exercise was to get the project deploying automatically to the cloud, and I wasn't particular about where. I decided that the <a href="https://www.windowsazure.com/en-us/home/scenarios/web-sites/" title="About the Azure Websites Feature">free website</a> feature in Azure would be a good target. Mostly due to the free part but also because I suspected it would be easy.</p>

<p>First up was creating an Azure account, which I did via <a href="https://www.windowsazure.com/en-us/" title="WindowsAzure.com">WindowsAzure.com</a>. The Web Sites feature is still in Preview mode, so I had to go the preview features and enable it.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-01.png" alt="WindowsAzure.com - Preview Features Option" /><br />
	WindowsAzure.com - Preview Features Option
</div>

<p>The Web Sites option is at the bottom and enabled with a button and dialog:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-02.png" alt="WindowsAzure.com - Preview Features" /><br />
	WindowsAzure.com - Preview Features
</div>

<p>Once it's enabled, I can go into the portal and when I press the "New" button at the bottom, I'll have options to create a new Web Site instance.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-03.png" alt="WindowsAzure.com - New Website" /><br />
	WindowsAzure.com - New Website
</div>

<p>After creating the new site, it will provision and show up in my dashboard:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-04.png" alt="WindowsAzure.com - Dashboard" /><br />
	WindowsAzure.com - Dashboard
</div>

<p>That's all it takes to have a new Web Site in Windows Azure. Of course it's not doing anything useful at the moment, so lets go back to the build process and fix that.</p>

<h2>Build Process Changes</h2>
<p>Because I chose to use an Azure Web Site, the build changes are going to be minimal. Microsoft intends us to use the Publish feature in Visual Studio or Web Matrix to deploy our websites, so they give us all the settings we need to run the msdeploy executable from a build step instead. </p>

<p>In the properties dashboard for the new website above, there is a link to download the publish settings for the site and a link to setup credentials. First setup the credentials, then click the download link to download the publish settings. These settings are intended for Visual Studio or Web Matrix, but being XML we can read the file and pluck out the important values.</p>

<p>I made a copy of my final "Deploy to Production" step and modified the msdeploy step to use a new Param file. The Param file is used during the deployment to set the IIS name and connection strings, you can see all of them on <a href="https://bitbucket.org/tarwn/mvcmusicstore.main/src/1560a5fd57ea/Configs" title="Parameter files at tarwn/MVCMusicStore.Main">BitBucket</a>. </p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-05.png" alt="TeamCity Build Steps" /><br />
	TeamCity Build Steps
</div>

<p><b>MSDeploy Command (TeamCity version):</b></p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb78015'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb78015','cb86250'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb78015" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&quot;C:\Program Files\IIS\Microsoft Web Deploy V2\msdeploy.exe&quot; -source:package='%system.teamcity.build.workingDir%\PriorArtifacts\MvcMusicStore.zip' -dest:auto,computerName='%COMPUTER%',userName='%USERNAME%',password='%PASSWORD%',includeAcls='False',authtype=basic -allowUntrusted &nbsp;-verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile=&quot;%system.teamcity.build.workingDir%\PriorArtifacts\MvcMusicStore.AZUREWEBSITE.xml&quot; -enableRule:DoNotDeleteRule</li></ol></div><div id="cb86250" style="display: none; color: red;"></div></div></div>

<p>I then updated the Configuration Parameters to reflect the Azure credentials and URL so the deployment and smoke test would work properly and entered the username and password entered in the Azure Dashboard in the prior step.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-07.png" alt="TeamCity Configuration Parameters" /><br />
	TeamCity Configuration Parameters
</div>

<p>And that's it, probably the fastest on premises to cloud change. Press the build button in TeamCity and we have a green build to the cloud:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-08.png" alt="TeamCity Build Successful" /><br />
	TeamCity Build Successful
</div>

<p>And after a similar set of changes in Jenkins, I have a parallel production step that targets Azure as well:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-9.png" alt="Jenkins Build Pipeline" /><br />
	Jenkins Build Pipeline
</div>

<p>And of course I had to push the button a few times:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/Azure-10.png" alt="Jenkins Build Success" /><br />
	Jenkins Build Success
</div>

<p>You can see the site here: <a href="http://elismvcmusicstore.azurewebsites.net">http://elismvcmusicstore.azurewebsites.net</a></p>

<p><em>Note: It will probably load slowly. It's on a shared node and seems to go to sleep since it gets visited so infrequently. If I felt like paying for it, it's just a toggle button away from running as a reserved instance (and another to scale it up to multiple instances)</em></p>

<h2>That was the easy button version</h2>
<p>Despite the fact that I chose the easiest possible target, as long as we can script the deployment we can easily add it into the pipeline and know that our deployment is taking exactly the same steps every time it deploys. Whether we were instead deploying to an Azure web role, deploying EC2 instances, or pushing to another provider, we have the framework to do so consistently and safely.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-to-the-cloud">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-to-the-cloud#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1837</wfw:commentRss>
		</item>
				<item>
			<title>Continuous Delivery with TeamCity</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity</link>
			<pubDate>Fri, 14 Sep 2012 12:11:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Application Lifecycle Management</category>			<guid isPermaLink="false">1827@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Over the series of 12 posts, I have built a &lt;a href=&quot;http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project&quot; title=&quot;Continuous Delivery wiki post&quot; target=&quot;_blank&quot;&gt;continuous delivery pipeline&lt;/a&gt; around the MVC Music Store tutorial web site. The journey included making changes to support unit testing, creating a CI build, adding automated multi-environment deployment, automated interface testing, automated load testing stage, and static analysis. Up until now, this was entirely on &lt;a href=&quot;http://jenkins-ci.org/&quot; title=&quot;Jenkins CI website&quot; target=&quot;_blank&quot;&gt;Jenkins&lt;/a&gt;, but today I intend to re-implement the pipeline on &lt;a href=&quot;http://www.jetbrains.com/teamcity/&quot; title=&quot;TeamCity by JetBrains&quot; target=&quot;_blank&quot;&gt;TeamCity&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;General Feelings&lt;/h2&gt;
&lt;p&gt;My general feelings towards TeamCity during this process have been good. I found several places where TeamCity would have been easier to use [than Jenkins], such as parsing test result files and evaluating results. I like the visibility of pending changes for each build step and everything I have implemented has been extremely straightforward (though I haven&#039;t taken on custom graphs and code coverage yet). Wiring together dependencies was far easier and clearer then in Jenkins, and the build-level configurations and sharing of build numbers throughout the build was a lot cleaner. Little touches like previews for the artifacts and working directories were also extremely handy.&lt;/p&gt;

&lt;p&gt;Not everything was perfect, however. The build chains were as close as I could get to a pipeline visualization and they are busier then the pipeline view in Jenkins. There also wasn&#039;t an obvious way to use a build chain view for the dashboard. Rerunning steps in the build would create a whole new chain, rather then being treated as a retry of a prior run of the same snapshot. The plugins don&#039;t feel quite as first class in TeamCity as they did in Jenkins, but the built in functionality was quite a bit more extensive, so I think this just comes down to focus and my own experience (or lack) with the tools. &lt;/p&gt;

&lt;p&gt;But enough of my feelings, lets do some technical stuff.&lt;/p&gt;

&lt;h2&gt;Review of the Build Process&lt;/h2&gt;
&lt;p&gt;The build pipeline is the process the code goes through from committing the code to eventual delivery, automated where possible to minimize the lead time for new features and wire in good practices I want to follow. The code is built and passes through a variety of tests and checks at each step, with only a few configurations changing along the way, ensuring what is released has passed every step as quickly and consistently as possible (smooth is fast). &lt;/p&gt;

&lt;p&gt;The current Jenkins build pipeline looks like this (plus lots of static analysis that doesn&#039;t fit in the picture):&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: .9em; color: #666666;&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/pipeline_load.png&quot; title=&quot;Continuous Delivery Pipeline with Load Test Step&quot; /&gt;&lt;br /&gt;
	Continuous Delivery Pipeline with Load Test Step
&lt;/div&gt;

&lt;p&gt;These are the portion I intend to do in TeamCity:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;CI Stage&lt;/dt&gt;&lt;dd&gt;Monitors main code repository, execute build, run unit tests, deploy to ensure deployable, smoke test deployment&lt;/dd&gt;
&lt;dt&gt;Interface Testing&lt;/dt&gt;&lt;dd&gt;Monitors interface test code repository, deploys + smoke tests deployment, runs interface tests&lt;/dd&gt;
&lt;dt&gt;Load Testing&lt;/dt&gt;&lt;dd&gt;Monitors load test code repository, deploys + smoke tests deployment, runs load test&lt;/dd&gt;
&lt;dt&gt;QA Deploy&lt;/dt&gt;&lt;dd&gt;Manual QA deployment for imaginary QA department&lt;/dd&gt;
&lt;dt&gt;Prod Deploy&lt;/dt&gt;&lt;dd&gt;Manual production deployment&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;I will be leaving the load test results graph and &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-static-analysis&quot; title=&quot;Continuous Delivery - Adding Static Analysis&quot; target=&quot;_blank&quot;&gt;static analysis&lt;/a&gt; until a later post.&lt;/p&gt;

&lt;h2&gt;Server Setup&lt;/h2&gt;
&lt;p&gt;Since my &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/starting-a-continuous-delivery-project&quot; title=&quot;Starting a Continuous Delivery Project&quot;&gt;original post&lt;/a&gt; I have setup a dedicated Hyper-V server on Windows 2012 RC and moved the original AVL-BUILD-01 and AVL-BETA-01 server to this host, adding an AVL-SQL-01 that serves as a host for environment-specific MSSQL databases. I created the new TeamCity build server (AVL-BUILD-02) to match the original, including same versions of OS, Visual Studio, etc:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Initial Installs:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 2008 R2 + lots of Windows updates&lt;/li&gt;
&lt;li&gt;Visual Studio 2010 + more updates (SP1, etc)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.asp.net/mvc/mvc3&quot; title=&quot;ASP.Net MVC 3 Download&quot; target=&quot;_blank&quot;&gt;ASP.Net MVC3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.jetbrains.com/teamcity/download/&quot; title=&quot;TeamCity download&quot; target=&quot;_blank&quot;&gt;TeamCity 7.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mercurial.selenic.com/downloads/&quot; title=&quot;Mercurial download&quot; target=&quot;_blank&quot;&gt;Mercurial 2.3.0&lt;/a&gt; (only version difference from AVL-BUILD-01)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.iis.net/learn/install/installing-publishing-technologies/installing-and-configuring-web-deploy&quot; title=&quot;Installing Web Deploy on iis.net&quot; target=&quot;_blank&quot;&gt;Web Deploy 2.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.iis.net/downloads/community/2007/05/wcat-63-%28x64%29&quot; title=&quot;WCAT 6.3 download on iis.net&quot; target=&quot;_blank&quot;&gt;WCAT 6.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.mozilla.org/en-US/firefox/new/&quot; title=&quot;Firefox download&quot; target=&quot;_blank&quot;&gt;FireFox 146 (or whatever)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As noted, the Firefox and mercurial installs didn&#039;t match, version-wise, but everything else I kept the same as the Jenkins build server.&lt;/p&gt;

&lt;h2&gt;Creating the CI Build&lt;/h2&gt;
&lt;p&gt;The CI project is probably the most complex one due to the number of steps and fact that it serves the artifacts every later step will use. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;1: Create the project&lt;/b&gt;&lt;br /&gt;
The project serves as the container for the builds, creating one only requires a name and optional description:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_01.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_01_sm.png&quot; alt=&quot;TeamCity - Project Setup&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Project Setup
&lt;/div&gt;

&lt;p&gt;Once the project is named, I can add my first build step (don&#039;t worry, I&#039;ll only do these screen shots one time):&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_02.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_02_sm.png&quot; alt=&quot;TeamCity - Ready to add builds&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Ready to add builds
&lt;/div&gt;

&lt;p&gt;Here I&#039;ll name the build step and define the artifacts I want to store for later use, including the deployment package I intend to make and the folder of environment-specific configurations.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_03.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_03_sm.png&quot; alt=&quot;TeamCity - Adding a Build&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Adding a Build
&lt;/div&gt;

&lt;p&gt;Next is the settings for the main site code repository. To create VCS settings for the build we need to create the VCS root (which includes the polling settings) then define the settings that are specific to this build. &lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_04.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_04_sm.png&quot; alt=&quot;TeamCity - Defining Version Control&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Defining Version Control
&lt;/div&gt;

&lt;p&gt;At this point I can start adding my build steps. These build steps will duplicate the ones I used in Jenkins. An advantage over the Jenkins setup is that the MSBuild runner already has versions configured and there is a built-in MSTest runner. When entering the command-line calls I used in Jenkins, I&#039;ll substitute in TeamCity variables using the small &lt;img src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityPopup.gif&quot; alt=&quot;Popup icon&quot; /&gt; image next to the inputs.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_05.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_05_sm.png&quot; alt=&quot;TeamCity - Build Steps&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Build Steps
&lt;/div&gt;

&lt;p&gt;Below the build steps I have used the report processing feature to process the results of the smoke test script (&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-project-deploy-and&quot; title=&quot;Continuous Delivery Project - Deploy and Smoke Test&quot;&gt;original post here&lt;/a&gt;), which outputs in JUnit format. TeamCity will automatically collect the results from the MSTest step and this step and make them available in the summary and run details.&lt;/p&gt;

&lt;p&gt;The second to last steps is to setup a build trigger to run on VCS changes, which is a couple clicks and extremely straightforward.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_06.png&quot; alt=&quot;TeamCity - Adding a Trigger&quot; /&gt;&lt;br /&gt;
	Team City - Adding a Trigger
&lt;/div&gt;

&lt;p&gt;Then the last step is to define the build parameters. TeamCity automatically detected the extra unrecognized parameters from my command-line build steps and has added them to the list of build configurations, which I thought was pretty slick. To make it even better, I can reference these parameters from later builds to reduce the number of places I need to define unique settings.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_07.png&quot; alt=&quot;TeamCity - Build Parameters&quot; /&gt;&lt;br /&gt;
	Team City - Build Parameters
&lt;/div&gt;

&lt;p&gt;Once I have finished setting up this build, I can return to the main dashboard and run it on demand by pressing the &quot;Run&quot; button.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_08.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_08_sm.png&quot; alt=&quot;TeamCity - Dashboard&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Dashboard
&lt;/div&gt;
&lt;p&gt;And we have the first build stage done.&lt;/p&gt;

&lt;h2&gt;Creating the Interface Testing Build&lt;/h2&gt;
&lt;p&gt;The interface testing build (&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-an-automated&quot; title=&quot;
Continuous Delivery - Adding an Automated Interface Test Stage&quot;&gt;original post here&lt;/a&gt;) is only a few steps. The first few stages of this build are the same, I create the build, define a VCS root that points to the Interface testing project on BitBucket, then add the build steps. I use an MSBuild step to build the automated interface test project (SpecFlow, Nunit, Selenium), command-line steps to call MSDeploy and the smoke test script, copy in the automated test settings for the tests, then use the built-in Nunit test runner to run the automated test assembly.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_09.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_09_sm.png&quot; alt=&quot;TeamCity - Interface Test Build&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Interface Test Build
&lt;/div&gt;

&lt;p&gt;Skipping ahead to the Dependencies section, I&#039;ll add a snapshot dependency on the CI stage so that builds of the Interface stage use snapshots from the same point in time as it&#039;s linked CI build. I&#039;ll also add an artifact dependency to pull all of the artifacts from the CI build into a local folder named PriorArtifacts, keeping with the pattern I used in the Jenkins build. &lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_10.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_10_sm.png&quot; alt=&quot;TeamCity - Dependencies&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Dependencies
&lt;/div&gt;

&lt;p&gt;Next I add a build trigger to run after a successful build of the CI stage and fill the dependencies with references to the values from the CI Build where I can.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_11.png&quot; alt=&quot;TeamCity - Build Parameters&quot; /&gt;&lt;br /&gt;
	Team City - Build Parameters
&lt;/div&gt;

&lt;p&gt;And the last step is to return to the project screen and define the Artifacts for this stage as &quot;PriorArtifacts/*&quot; so it will pass on all of the artifacts it pulled in from the prior stage.&lt;/p&gt;

&lt;p&gt;Running this build reminded me that Firefox has a nasty habit of downloading updates when you least want them, as the interface tests started timing out because they couldn&#039;t quit. A quick tweak to the Firefox settings and I have a successful interface test step running, with the test results showing in the summary just like the CI stage.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_12.png&quot; alt=&quot;TeamCity - Interface Tests&quot; /&gt;&lt;br /&gt;
	Team City - Interface Tests
&lt;/div&gt;

&lt;p&gt;Note: The build number for each build allows you to define your own pattern. By setting it to %dep.bt2.build.number%, each of my builds use the build number I created in the CI stage (which is used again in the smoke tests).&lt;/p&gt;

&lt;h2&gt;Creating the Load Test, QA, and Prod Builds&lt;/h2&gt;
&lt;p&gt;At this point I have all the pieces I need to finish out the last 3 stages.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Load Test Build&lt;/b&gt;&lt;br /&gt;
The Load Test stage (&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-the-load&quot; title=&quot;Continuous Delivery - Load Test Stage&quot;&gt;see original here&lt;/a&gt;) gets the same build number and artifacts as the Interface Testing stage. I add a new VCS root to pull down the load tests scripts from &lt;a href=&quot;https://bitbucket.org/tarwn/mvcmusicstore.loadtest&quot; title=&quot;My load test repository on bitbucket&quot;&gt;BitBucket&lt;/a&gt;, deploy, smoke test, then run the run.cmd file that runs the WCAT load test. I skipped the challenging part of this, which is turning the results into a chart, but as I mentioned earlier that sounds like it will be a follow-up post of it&#039;s own. The dependencies, parameters, and build trigger are set up the same as the Interface testing build.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Deploy QA and Deploy Prod Builds&lt;/b&gt;&lt;br /&gt;
The last two are rinse and repeats. Take the build number and artifact settings, no VCS settings, add dependencies and leave them to be triggered manually.&lt;/p&gt;

&lt;h2&gt;The Pipeline&lt;/h2&gt;
&lt;p&gt;Those 5 builds compromise the core of the pipeline and show up in the TeamCity dashboard like so:&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_13.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_13_sm.png&quot; alt=&quot;TeamCity - Full Dashboard&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Full Dashboard
&lt;/div&gt;

&lt;p&gt;To get to the build chains view, go into the Project page and click the last tab to the right, titled &quot;Build Chains&quot;.&lt;/p&gt;

&lt;div style=&quot;text-align: center; font-size: 90%; color: #666666;&quot;&gt;
	&lt;a href=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_14.png&quot; target=&quot;_blank&quot;&gt;&lt;br /&gt;
		&lt;img style=&quot;border: 1px solid #999999&quot; src=&quot;http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_14_sm.png&quot; alt=&quot;TeamCity - Build Chains&quot; /&gt;&lt;br /&gt;
	&lt;/a&gt;&lt;br /&gt;
	Team City - Build Chains
&lt;/div&gt;

&lt;p&gt;The summary bar lists the final step and status in the chain, the last step that ran, and the date. Expanding it shows the full chain (though there are some scrolling issues).&lt;/p&gt;

&lt;h2&gt;Last Words&lt;/h2&gt;
&lt;p&gt;This was an interesting project, as I was able to take a fairly complex build and see how it would work in a completely different engine. The addition of a first class pipeline view is probably the biggest item on my wishlist and I look forward to seeing what they do with it in the future. Even without that, though, it was a great tool to use and offered me none of the teething problems I had with several of the Jenkins plugins.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Over the series of 12 posts, I have built a <a href="http://wiki.lessthandot.com/index.php/Eli%27s_Continuous_Delivery_Project" title="Continuous Delivery wiki post" target="_blank">continuous delivery pipeline</a> around the MVC Music Store tutorial web site. The journey included making changes to support unit testing, creating a CI build, adding automated multi-environment deployment, automated interface testing, automated load testing stage, and static analysis. Up until now, this was entirely on <a href="http://jenkins-ci.org/" title="Jenkins CI website" target="_blank">Jenkins</a>, but today I intend to re-implement the pipeline on <a href="http://www.jetbrains.com/teamcity/" title="TeamCity by JetBrains" target="_blank">TeamCity</a>.</p>

<h2>General Feelings</h2>
<p>My general feelings towards TeamCity during this process have been good. I found several places where TeamCity would have been easier to use [than Jenkins], such as parsing test result files and evaluating results. I like the visibility of pending changes for each build step and everything I have implemented has been extremely straightforward (though I haven't taken on custom graphs and code coverage yet). Wiring together dependencies was far easier and clearer then in Jenkins, and the build-level configurations and sharing of build numbers throughout the build was a lot cleaner. Little touches like previews for the artifacts and working directories were also extremely handy.</p>

<p>Not everything was perfect, however. The build chains were as close as I could get to a pipeline visualization and they are busier then the pipeline view in Jenkins. There also wasn't an obvious way to use a build chain view for the dashboard. Rerunning steps in the build would create a whole new chain, rather then being treated as a retry of a prior run of the same snapshot. The plugins don't feel quite as first class in TeamCity as they did in Jenkins, but the built in functionality was quite a bit more extensive, so I think this just comes down to focus and my own experience (or lack) with the tools. </p>

<p>But enough of my feelings, lets do some technical stuff.</p>

<h2>Review of the Build Process</h2>
<p>The build pipeline is the process the code goes through from committing the code to eventual delivery, automated where possible to minimize the lead time for new features and wire in good practices I want to follow. The code is built and passes through a variety of tests and checks at each step, with only a few configurations changing along the way, ensuring what is released has passed every step as quickly and consistently as possible (smooth is fast). </p>

<p>The current Jenkins build pipeline looks like this (plus lots of static analysis that doesn't fit in the picture):</p>

<div style="text-align: center; font-size: .9em; color: #666666;">
	<img src="http://tiernok.com/LTDBlog/ContinuousDelivery/pipeline_load.png" title="Continuous Delivery Pipeline with Load Test Step" /><br />
	Continuous Delivery Pipeline with Load Test Step
</div>

<p>These are the portion I intend to do in TeamCity:</p>

<dl>
<dt>CI Stage</dt><dd>Monitors main code repository, execute build, run unit tests, deploy to ensure deployable, smoke test deployment</dd>
<dt>Interface Testing</dt><dd>Monitors interface test code repository, deploys + smoke tests deployment, runs interface tests</dd>
<dt>Load Testing</dt><dd>Monitors load test code repository, deploys + smoke tests deployment, runs load test</dd>
<dt>QA Deploy</dt><dd>Manual QA deployment for imaginary QA department</dd>
<dt>Prod Deploy</dt><dd>Manual production deployment</dd>
</dl>

<p>I will be leaving the load test results graph and <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-static-analysis" title="Continuous Delivery - Adding Static Analysis" target="_blank">static analysis</a> until a later post.</p>

<h2>Server Setup</h2>
<p>Since my <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/starting-a-continuous-delivery-project" title="Starting a Continuous Delivery Project">original post</a> I have setup a dedicated Hyper-V server on Windows 2012 RC and moved the original AVL-BUILD-01 and AVL-BETA-01 server to this host, adding an AVL-SQL-01 that serves as a host for environment-specific MSSQL databases. I created the new TeamCity build server (AVL-BUILD-02) to match the original, including same versions of OS, Visual Studio, etc:</p>

<p><b>Initial Installs:</b></p>
<ul>
<li>Windows 2008 R2 + lots of Windows updates</li>
<li>Visual Studio 2010 + more updates (SP1, etc)</li>
<li><a href="http://www.asp.net/mvc/mvc3" title="ASP.Net MVC 3 Download" target="_blank">ASP.Net MVC3</a></li>
<li><a href="http://www.jetbrains.com/teamcity/download/" title="TeamCity download" target="_blank">TeamCity 7.1</a></li>
<li><a href="http://mercurial.selenic.com/downloads/" title="Mercurial download" target="_blank">Mercurial 2.3.0</a> (only version difference from AVL-BUILD-01)</li>
<li><a href="http://www.iis.net/learn/install/installing-publishing-technologies/installing-and-configuring-web-deploy" title="Installing Web Deploy on iis.net" target="_blank">Web Deploy 2.1</a></li>
<li><a href="http://www.iis.net/downloads/community/2007/05/wcat-63-%28x64%29" title="WCAT 6.3 download on iis.net" target="_blank">WCAT 6.3</a></li>
<li><a href="http://www.mozilla.org/en-US/firefox/new/" title="Firefox download" target="_blank">FireFox 146 (or whatever)</a></li>
</ul>

<p>As noted, the Firefox and mercurial installs didn't match, version-wise, but everything else I kept the same as the Jenkins build server.</p>

<h2>Creating the CI Build</h2>
<p>The CI project is probably the most complex one due to the number of steps and fact that it serves the artifacts every later step will use. </p>

<p><b>1: Create the project</b><br />
The project serves as the container for the builds, creating one only requires a name and optional description:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_01.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_01_sm.png" alt="TeamCity - Project Setup" /><br />
	</a><br />
	Team City - Project Setup
</div>

<p>Once the project is named, I can add my first build step (don't worry, I'll only do these screen shots one time):</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_02.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_02_sm.png" alt="TeamCity - Ready to add builds" /><br />
	</a><br />
	Team City - Ready to add builds
</div>

<p>Here I'll name the build step and define the artifacts I want to store for later use, including the deployment package I intend to make and the folder of environment-specific configurations.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_03.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_03_sm.png" alt="TeamCity - Adding a Build" /><br />
	</a><br />
	Team City - Adding a Build
</div>

<p>Next is the settings for the main site code repository. To create VCS settings for the build we need to create the VCS root (which includes the polling settings) then define the settings that are specific to this build. </p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_04.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_04_sm.png" alt="TeamCity - Defining Version Control" /><br />
	</a><br />
	Team City - Defining Version Control
</div>

<p>At this point I can start adding my build steps. These build steps will duplicate the ones I used in Jenkins. An advantage over the Jenkins setup is that the MSBuild runner already has versions configured and there is a built-in MSTest runner. When entering the command-line calls I used in Jenkins, I'll substitute in TeamCity variables using the small <img src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCityPopup.gif" alt="Popup icon" /> image next to the inputs.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_05.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_05_sm.png" alt="TeamCity - Build Steps" /><br />
	</a><br />
	Team City - Build Steps
</div>

<p>Below the build steps I have used the report processing feature to process the results of the smoke test script (<a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-project-deploy-and" title="Continuous Delivery Project - Deploy and Smoke Test">original post here</a>), which outputs in JUnit format. TeamCity will automatically collect the results from the MSTest step and this step and make them available in the summary and run details.</p>

<p>The second to last steps is to setup a build trigger to run on VCS changes, which is a couple clicks and extremely straightforward.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_06.png" alt="TeamCity - Adding a Trigger" /><br />
	Team City - Adding a Trigger
</div>

<p>Then the last step is to define the build parameters. TeamCity automatically detected the extra unrecognized parameters from my command-line build steps and has added them to the list of build configurations, which I thought was pretty slick. To make it even better, I can reference these parameters from later builds to reduce the number of places I need to define unique settings.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_07.png" alt="TeamCity - Build Parameters" /><br />
	Team City - Build Parameters
</div>

<p>Once I have finished setting up this build, I can return to the main dashboard and run it on demand by pressing the "Run" button.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_08.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_08_sm.png" alt="TeamCity - Dashboard" /><br />
	</a><br />
	Team City - Dashboard
</div>
<p>And we have the first build stage done.</p>

<h2>Creating the Interface Testing Build</h2>
<p>The interface testing build (<a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-an-automated" title="
Continuous Delivery - Adding an Automated Interface Test Stage">original post here</a>) is only a few steps. The first few stages of this build are the same, I create the build, define a VCS root that points to the Interface testing project on BitBucket, then add the build steps. I use an MSBuild step to build the automated interface test project (SpecFlow, Nunit, Selenium), command-line steps to call MSDeploy and the smoke test script, copy in the automated test settings for the tests, then use the built-in Nunit test runner to run the automated test assembly.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_09.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_09_sm.png" alt="TeamCity - Interface Test Build" /><br />
	</a><br />
	Team City - Interface Test Build
</div>

<p>Skipping ahead to the Dependencies section, I'll add a snapshot dependency on the CI stage so that builds of the Interface stage use snapshots from the same point in time as it's linked CI build. I'll also add an artifact dependency to pull all of the artifacts from the CI build into a local folder named PriorArtifacts, keeping with the pattern I used in the Jenkins build. </p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_10.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_10_sm.png" alt="TeamCity - Dependencies" /><br />
	</a><br />
	Team City - Dependencies
</div>

<p>Next I add a build trigger to run after a successful build of the CI stage and fill the dependencies with references to the values from the CI Build where I can.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_11.png" alt="TeamCity - Build Parameters" /><br />
	Team City - Build Parameters
</div>

<p>And the last step is to return to the project screen and define the Artifacts for this stage as "PriorArtifacts/*" so it will pass on all of the artifacts it pulled in from the prior stage.</p>

<p>Running this build reminded me that Firefox has a nasty habit of downloading updates when you least want them, as the interface tests started timing out because they couldn't quit. A quick tweak to the Firefox settings and I have a successful interface test step running, with the test results showing in the summary just like the CI stage.</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_12.png" alt="TeamCity - Interface Tests" /><br />
	Team City - Interface Tests
</div>

<p>Note: The build number for each build allows you to define your own pattern. By setting it to %dep.bt2.build.number%, each of my builds use the build number I created in the CI stage (which is used again in the smoke tests).</p>

<h2>Creating the Load Test, QA, and Prod Builds</h2>
<p>At this point I have all the pieces I need to finish out the last 3 stages.</p>

<p><b>Load Test Build</b><br />
The Load Test stage (<a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-adding-the-load" title="Continuous Delivery - Load Test Stage">see original here</a>) gets the same build number and artifacts as the Interface Testing stage. I add a new VCS root to pull down the load tests scripts from <a href="https://bitbucket.org/tarwn/mvcmusicstore.loadtest" title="My load test repository on bitbucket">BitBucket</a>, deploy, smoke test, then run the run.cmd file that runs the WCAT load test. I skipped the challenging part of this, which is turning the results into a chart, but as I mentioned earlier that sounds like it will be a follow-up post of it's own. The dependencies, parameters, and build trigger are set up the same as the Interface testing build.</p>

<p><b>Deploy QA and Deploy Prod Builds</b><br />
The last two are rinse and repeats. Take the build number and artifact settings, no VCS settings, add dependencies and leave them to be triggered manually.</p>

<h2>The Pipeline</h2>
<p>Those 5 builds compromise the core of the pipeline and show up in the TeamCity dashboard like so:</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_13.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_13_sm.png" alt="TeamCity - Full Dashboard" /><br />
	</a><br />
	Team City - Full Dashboard
</div>

<p>To get to the build chains view, go into the Project page and click the last tab to the right, titled "Build Chains".</p>

<div style="text-align: center; font-size: 90%; color: #666666;">
	<a href="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_14.png" target="_blank"><br />
		<img style="border: 1px solid #999999" src="http://tiernok.com/LTDBlog/ContinuousDelivery/TeamCity_14_sm.png" alt="TeamCity - Build Chains" /><br />
	</a><br />
	Team City - Build Chains
</div>

<p>The summary bar lists the final step and status in the chain, the last step that ran, and the date. Expanding it shows the full chain (though there are some scrolling issues).</p>

<h2>Last Words</h2>
<p>This was an interesting project, as I was able to take a fairly complex build and see how it would work in a completely different engine. The addition of a first class pipeline view is probably the biggest item on my wishlist and I look forward to seeing what they do with it in the future. Even without that, though, it was a great tool to use and offered me none of the teething problems I had with several of the Jenkins plugins.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/application-lifecycle-management/continuous-delivery-with-teamcity#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1827</wfw:commentRss>
		</item>
				<item>
			<title>Metrics as a Service - Librato Metrics</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/metrics-as-a-service-librato</link>
			<pubDate>Wed, 15 Aug 2012 10:06:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Instrumentation</category>			<guid isPermaLink="false">1792@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Several weeks ago I posted a &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service&quot; title=&quot;Monitoring and Logging as a Service - Introduction&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits&quot; title=&quot;Monitoring and Logging as a Service - The Common Bits&quot;&gt;part&lt;/a&gt; &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews&quot; title=&quot;Monitoring and Logging as a Service - Reviews&quot;&gt;series&lt;/a&gt; on the ability to use available online logging services for instrumenting applications. None of the services overwhelmed me, but there was an additional service I found that caters specifically to numeric metrics. Given that I&#039;ve had a tab sitting open on the &lt;a href=&quot;https://metrics.librato.com/&quot;&gt;Librato site&lt;/a&gt; for the past several weeks, I thought it was time to actually do the review.&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/instrumentation/metrics_0.png&quot; alt=&quot;Sample screenshot from interface&quot; /&gt;&lt;br /&gt;
	Sample screenshot from interface
&lt;/div&gt;

&lt;p&gt;Spoilers: I liked it. A lot.&lt;/p&gt;

&lt;h2&gt;About Librato Metrics&lt;/h2&gt;
&lt;p&gt;Unlike the unstructured data logging services in the prior posts, Metrics is all about storing, displaying, and alerting on numeric metric data. This limits how you would use it, for instance you wouldn&#039;t be able to dig into a spike in page execution times and see the id&#039;s of the users or the type of browser that was in use. On the other hand, the focus on numeric gauges and counters means that Librato can provide a deeper set of tools around graphing, alerting, and dashboards. &lt;/p&gt;

&lt;h3&gt;Highlights:&lt;/h3&gt;
&lt;p&gt;Librato&#039;s API is extremely full-featured, offering GET/PUT/POST actions for creating, sending, and retrieving metrics, as well as endpoints to interact with other facets of the service, like dashboards. All of this is offered through an interface that is both polished and usable, with none of the lag I ran into on some of the other services. In addition, support for alerting is built into the application.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Input Support:&lt;/b&gt; HTTP GET/PUT/POST/DELETE everything (see note below) with JSON&lt;br /&gt;
&lt;b&gt;Output Support:&lt;/b&gt; Same as above, plus has support for paginating large sets of results&lt;br /&gt;
&lt;b&gt;Ease of Setup:&lt;/b&gt; Very easy through the interface or by just sending new data metrics, API token is immediately available for use&lt;br /&gt;
&lt;b&gt;Searching:&lt;/b&gt; This is not a log service, so the searching only covers searching for entities (metrics, dashboards, etc)&lt;br /&gt;
&lt;b&gt;Visualization:&lt;/b&gt; Great. Though they currently only have support for line graphs, the overall interface is fantastic and there are other graph types in the works&lt;/p&gt;

&lt;p&gt;Note: The full list of elements you can interact with via the API includes Metrics, Instruments, Dashboards, Tags, Alerts, Services, and even Users.&lt;/p&gt;

&lt;h3&gt;General Opinion:&lt;/h3&gt;
&lt;p&gt;This service is easy to use, provides a really usable interface, and is completely open and extensible through a very rich API. Despite some hinkiness with the dashboard timespans, which I think is due to the small amount of sample data I had and their down-sampling method, this is an excellent service. The services also support sending data aggregated over a time period (multi-sample), so you can buffer and send at longer intervals if the finer detail is an unnecessary drain on your wallet or network.&lt;/p&gt;

&lt;p&gt;I suspect that their interface uses the same API that they expose to end users, as I haven&#039;t been able to find anything I could do in the interface that wasn&#039;t also possible in the API. Exposing the data through the API also means that not only do we have all of the graphic and alerting capabilities that are built in to the service, we can also extend it to do more (something several of the other reviewed services required to even get a simple graph). &lt;/p&gt;

&lt;p&gt;Getting the sample application hooked into the service was extremely easy, even taking into account the fact that I had to write a parallel set of methods to send the metric data instead of the log data for the other services.&lt;/p&gt;

&lt;h3&gt;Running the Sample Site with Metrics&lt;/h3&gt;
&lt;p&gt;Clone a copy of the sample application from &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode&quot; title=&quot;InstrumentationSampleCode on GitHub&quot;&gt;tarwn/InstrumentationSampleCode on github&lt;/a&gt;. Go to the &lt;a href=&quot;https://metrics.librato.com/&quot; target=&quot;_blank&quot;&gt;metrics.librato.com website&lt;/a&gt; and sign up for an account (they have a 30 day free trial). &lt;/p&gt;

&lt;p&gt;After signing in to your account, you can access your account information from the &quot;Account Settings&quot; link at the bottom of the screen. Add or edit the &lt;i&gt;sensitive.config&lt;/i&gt; file at the root of the .Net solution, substituting your values where appropriate:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;sensitive.config&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb32857&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;MetricProvider = Librato&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Librato.URL = &amp;lt;a href=&amp;quot;https://metrics-api.librato.com/v1/metrics&amp;quot;&amp;gt;https://metrics-api.librato.com/v1/metrics&amp;lt;/a&amp;gt;&lt;br /&gt;Librato.Email = your-email-here&lt;br /&gt;Librato.Token = your-token-here&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb26578&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Load up the sample app, click some buttons, then switch over to Metrics to see the results. You should see several new gauges and counters in your Metrics list: &lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/instrumentation/metrics_01.png&quot; alt=&quot;Metrics List&quot; /&gt;&lt;br /&gt;
	Metrics List
&lt;/div&gt;


&lt;p&gt;Clicking on one of these metrics will open the metric so you can see it&#039;s display settings and a graph of it&#039;s recent values:&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/instrumentation/metrics_2.png&quot; alt=&quot;Metric Details&quot; /&gt;&lt;br /&gt;
	Metric Details
&lt;/div&gt;

&lt;p&gt;We could spend a lot more time getting into the details of creating instruments, dashboards, alerts and so on, but at this point you should have everything you need to start playing with those on your own.&lt;/p&gt;

&lt;p&gt;I&#039;ve really enjoyed using Metrics so far, and if you&#039;re looking for a metrics tool you should definitely give it a test drive.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/metrics-as-a-service-librato&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Several weeks ago I posted a <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service" title="Monitoring and Logging as a Service - Introduction">three</a> <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits" title="Monitoring and Logging as a Service - The Common Bits">part</a> <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews" title="Monitoring and Logging as a Service - Reviews">series</a> on the ability to use available online logging services for instrumenting applications. None of the services overwhelmed me, but there was an additional service I found that caters specifically to numeric metrics. Given that I've had a tab sitting open on the <a href="https://metrics.librato.com/">Librato site</a> for the past several weeks, I thought it was time to actually do the review.</p>

<div style="color: #666666; text-align: center; font-size: 90%">
	<img src="http://tiernok.com/LTDBlog/instrumentation/metrics_0.png" alt="Sample screenshot from interface" /><br />
	Sample screenshot from interface
</div>

<p>Spoilers: I liked it. A lot.</p>

<h2>About Librato Metrics</h2>
<p>Unlike the unstructured data logging services in the prior posts, Metrics is all about storing, displaying, and alerting on numeric metric data. This limits how you would use it, for instance you wouldn't be able to dig into a spike in page execution times and see the id's of the users or the type of browser that was in use. On the other hand, the focus on numeric gauges and counters means that Librato can provide a deeper set of tools around graphing, alerting, and dashboards. </p>

<h3>Highlights:</h3>
<p>Librato's API is extremely full-featured, offering GET/PUT/POST actions for creating, sending, and retrieving metrics, as well as endpoints to interact with other facets of the service, like dashboards. All of this is offered through an interface that is both polished and usable, with none of the lag I ran into on some of the other services. In addition, support for alerting is built into the application.</p>

<p><b>Input Support:</b> HTTP GET/PUT/POST/DELETE everything (see note below) with JSON<br />
<b>Output Support:</b> Same as above, plus has support for paginating large sets of results<br />
<b>Ease of Setup:</b> Very easy through the interface or by just sending new data metrics, API token is immediately available for use<br />
<b>Searching:</b> This is not a log service, so the searching only covers searching for entities (metrics, dashboards, etc)<br />
<b>Visualization:</b> Great. Though they currently only have support for line graphs, the overall interface is fantastic and there are other graph types in the works</p>

<p>Note: The full list of elements you can interact with via the API includes Metrics, Instruments, Dashboards, Tags, Alerts, Services, and even Users.</p>

<h3>General Opinion:</h3>
<p>This service is easy to use, provides a really usable interface, and is completely open and extensible through a very rich API. Despite some hinkiness with the dashboard timespans, which I think is due to the small amount of sample data I had and their down-sampling method, this is an excellent service. The services also support sending data aggregated over a time period (multi-sample), so you can buffer and send at longer intervals if the finer detail is an unnecessary drain on your wallet or network.</p>

<p>I suspect that their interface uses the same API that they expose to end users, as I haven't been able to find anything I could do in the interface that wasn't also possible in the API. Exposing the data through the API also means that not only do we have all of the graphic and alerting capabilities that are built in to the service, we can also extend it to do more (something several of the other reviewed services required to even get a simple graph). </p>

<p>Getting the sample application hooked into the service was extremely easy, even taking into account the fact that I had to write a parallel set of methods to send the metric data instead of the log data for the other services.</p>

<h3>Running the Sample Site with Metrics</h3>
<p>Clone a copy of the sample application from <a href="https://github.com/tarwn/InstrumentationSampleCode" title="InstrumentationSampleCode on GitHub">tarwn/InstrumentationSampleCode on github</a>. Go to the <a href="https://metrics.librato.com/" target="_blank">metrics.librato.com website</a> and sign up for an account (they have a 30 day free trial). </p>

<p>After signing in to your account, you can access your account information from the "Account Settings" link at the bottom of the screen. Add or edit the <i>sensitive.config</i> file at the root of the .Net solution, substituting your values where appropriate:</p>

<p><b>sensitive.config</b></p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb69621'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb69621','cb46374'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb69621" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">MetricProvider = Librato</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">Librato.URL = &lt;a href=&quot;https://metrics-api.librato.com/v1/metrics&quot;&gt;https://metrics-api.librato.com/v1/metrics&lt;/a&gt;</li><li style="" class="li2">Librato.Email = your-email-here</li><li style="" class="li1">Librato.Token = your-token-here</li></ol></div><div id="cb46374" style="display: none; color: red;"></div></div></div>

<p>Load up the sample app, click some buttons, then switch over to Metrics to see the results. You should see several new gauges and counters in your Metrics list: </p>

<div style="color: #666666; text-align: center; font-size: 90%">
	<img src="http://tiernok.com/LTDBlog/instrumentation/metrics_01.png" alt="Metrics List" /><br />
	Metrics List
</div>


<p>Clicking on one of these metrics will open the metric so you can see it's display settings and a graph of it's recent values:</p>

<div style="color: #666666; text-align: center; font-size: 90%">
	<img src="http://tiernok.com/LTDBlog/instrumentation/metrics_2.png" alt="Metric Details" /><br />
	Metric Details
</div>

<p>We could spend a lot more time getting into the details of creating instruments, dashboards, alerts and so on, but at this point you should have everything you need to start playing with those on your own.</p>

<p>I've really enjoyed using Metrics so far, and if you're looking for a metrics tool you should definitely give it a test drive.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/metrics-as-a-service-librato">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/metrics-as-a-service-librato#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1792</wfw:commentRss>
		</item>
				<item>
			<title>Followup on ORMs for Batch Performance</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/followup-on-orms-for-batch</link>
			<pubDate>Mon, 06 Aug 2012 16:02:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">ORM</category>			<guid isPermaLink="false">1793@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;A few weeks ago I looked at a project by Luke McGregor (&lt;a href=&quot;http://blog.staticvoid.co.nz/&quot; title=&quot;static void; blog&quot;&gt;blog&lt;/a&gt;|&lt;a href=&quot;https://twitter.com/staticv0id&quot; title=&quot;staticv0id on twitter&quot;&gt;twitter&lt;/a&gt;) that benchmarks a variety of ORMs doing common operations at the 1 to 10,000 record scales. I was curious to see how the ORMs he had included would fare against common ADO methods and how those ADO methods would compare to one another. &lt;/p&gt;

&lt;p&gt;My Original Post: &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data&quot; title=&quot;Evaluating ORMs for Batch Data Performance&quot;&gt;Evaluating ORMs for Batch Data Performance&lt;/a&gt;&lt;br /&gt;
Source Code: &lt;a href=&quot;https://github.com/tarwn/StaticVoid.OrmPerformance&quot; title=&quot;My fork of Luke&#039;s project on Github&quot;&gt;My fork of StaticVoid.OrmPerformance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I received a lot of good feedback and suggestions on that post, and implemented all of the additions (except Kermit&#039;s, oops). While I am still looking primarily at bulk insert performance, I also implemented the other test operations and Luke has pulled most of the changes back into his project (I am behind on issuing a pull request for NHibernate, sorry). This post is a follow-up on those additions with an updated conclusion, if you hadn&#039;t read the &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data&quot; title=&quot;Evaluating ORMs for Batch Data Performance&quot;&gt;previous post&lt;/a&gt;, it&#039;s probably still worth a read.&lt;/p&gt;

&lt;h2&gt;The Updated Lineup&lt;/h2&gt;

&lt;p&gt;The updated lineup for tests now looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EntityFramework 4.1&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;AutoDetectChanges Disabled&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;EntityFramework 5.0 beta1&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;AutoDetectChanges Disabled&lt;/li&gt;
	&lt;li&gt;Proxy Entities&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Dapper 1.8&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Dapper Rainbow&lt;/li&gt;
	&lt;li&gt;My Best effort at making it go fast&lt;/li&gt;
	&lt;li&gt;Batching inserts/updates using transactions&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;LINQ to SQL&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;NHibernate 3.2.0.4&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic implementation&lt;/li&gt;
	&lt;li&gt;Stateless + 200 record batches&lt;/li&gt;
	&lt;li&gt;Stateless + 1000 record batches&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;PetaPoco 3.04&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic implementation&lt;/li&gt;
	&lt;li&gt;Batching inserts via transaction&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Simple.Data 1.0.0 rc 0&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic implementation&lt;/li&gt;
	&lt;li&gt;Batch inserts (built-in, automagical)&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Raw ADO methods&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic SQL Command&lt;/li&gt;
	&lt;li&gt;SQL Command w/ Transaction&lt;/li&gt;
	&lt;li&gt;Bulk insert via SqlDataAdapter&lt;/li&gt;
	&lt;li&gt;Bulk insert via SqlBulkCopy and SqlBulkCopy w/ Table Lock option&lt;/li&gt;
	&lt;li&gt;Bulk insert via one really big SQL string (single insert w/ lots of concatenated value statements)&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along the way I also updated the project to include more information in failing assertions (the state is asserted after each test is run).&lt;/p&gt;

&lt;h2&gt;The Results&lt;/h2&gt;
&lt;p&gt;I was simultaneously surprised and not surprised at the results. With Mark Rendle (&lt;a href=&quot;http://blog.markrendle.net/&quot; title=&quot;Mark&#039;s blog&quot;&gt;blog&lt;/a&gt;|&lt;a href=&quot;https://twitter.com/markrendle&quot; title=&quot;MarkRendle on twitter&quot;&gt;twitter&lt;/a&gt;) suggesting I add Simple.Data, I fully expected it to keep up with SqlBulkCopy, but it was still surprising to see the real, raw data.&lt;/p&gt;

&lt;div style=&quot;text-align: center; color: #666666; font-size: 90%&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/GraphB-1.png&quot; alt=&quot;Graph of best bulk insert times for each major method&quot; /&gt;&lt;br /&gt;
		10,000 row inserts, Best times for each category
&lt;/div&gt;

&lt;p&gt;Raw SqlBulkCopy was still the best option for 10,000 records (this is the TabLock option variant), but Simple.Data was right up there with it. PetaPoco and NHibernate are our other new additions and they were fairly close, about 20-25% slower than Dapper, or about 7x the SqlBulkCopy test.&lt;/p&gt;

&lt;p&gt;But the important item on this graph really is Simple.Data. There are many that argue that all ORMs are slow and not worth spending time on but this is clearly not so. Simple.Data was a little slower than SqlBulkCopy and much faster than the other raw ADO variants. And it does this with no column mappings, no tricks and no special configurations. That is a huge milestone for ORMs and makes it tempting to use instead of SqlBulkCopy, as this method may actually be less fragile (no column mappings have to be manually defined).&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb10925&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; BatchConfiguration&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IConnectionString connectionString&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _db = Database.&lt;span style=&quot;color: #0000FF;&quot;&gt;OpenConnection&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;connectionString.&lt;span style=&quot;color: #0000FF;&quot;&gt;FormattedConnectionString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _entitiesToInsert = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; List&amp;lt;TestEntity&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Add&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;TestEntity entity&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _entitiesToInsert.&lt;span style=&quot;color: #0000FF;&quot;&gt;Add&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;entity&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Commit&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; _db.&lt;span style=&quot;color: #0000FF;&quot;&gt;TestEntities&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Insert&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;_entitiesToInsert&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb86478&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a connection, add as many items as you want to the list, then call Insert on the whole list. Magic.&lt;/p&gt;

&lt;h2&gt;More Detail&lt;/h2&gt;
&lt;p&gt;In the last post I included a comparison of all of the various methods used to insert data as a single graph. Unfortunately that graph was hard to read, as the basic Entity Framework methods were operating about an order of magnitude slower than everything else. I still thought the data was interesting, though, so I have created a graph with those values truncated as I did in the original post&#039;s followup. The one critical difference is that I am plotting the 10,000 record inserts instead of 100,000 like last time. That just took too long to run &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;

&lt;div style=&quot;text-align: center; color: #666666; font-size: 90%&quot;&gt;
	&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/GraphB-2.png&quot; alt=&quot;10,000 row inserts, All categories and variants&quot; /&gt;&lt;br /&gt;
		10,000 row inserts, All categories and variants
&lt;/div&gt;

&lt;p&gt;With more details we can see the spread and variation a little more clearly. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Big &#039;Ol String method:&lt;/b&gt; One of the other surprises I ran into when doing this was how slow the &quot;cram it all into one long string&quot; method was (&quot;SqlCommand - Insert Once&quot; above). This is often suggested as a quick and dirty way to do batch insertion, basically by concatenating all the data into a single long INSERT statement with multiple VALUE rows or UNIONs. At 100 records this option tied with Dapper as the fastest method. Unfortunately it scales really horribly, moving up to nearly last, leading me to believe that people suggesting this method only tested it in lower ranges.&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;In my last post I concluded that I wouldn&#039;t use an ORM for batch processing, but Simple.Data has forced me to reconsider that statement. Raw speed is not the only measure I would use to select a tool for getting data A into database B, but it is clear that if my requirements include bulk data insertion, ORMs are no longer automatically taken off the table. Congratulations to Mark for making a very clean, very fast way to get a whole lot of data into a database for very little up front effort (and thanks for adding it to my todo list).&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/followup-on-orms-for-batch&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>A few weeks ago I looked at a project by Luke McGregor (<a href="http://blog.staticvoid.co.nz/" title="static void; blog">blog</a>|<a href="https://twitter.com/staticv0id" title="staticv0id on twitter">twitter</a>) that benchmarks a variety of ORMs doing common operations at the 1 to 10,000 record scales. I was curious to see how the ORMs he had included would fare against common ADO methods and how those ADO methods would compare to one another. </p>

<p>My Original Post: <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data" title="Evaluating ORMs for Batch Data Performance">Evaluating ORMs for Batch Data Performance</a><br />
Source Code: <a href="https://github.com/tarwn/StaticVoid.OrmPerformance" title="My fork of Luke's project on Github">My fork of StaticVoid.OrmPerformance</a></p>

<p>I received a lot of good feedback and suggestions on that post, and implemented all of the additions (except Kermit's, oops). While I am still looking primarily at bulk insert performance, I also implemented the other test operations and Luke has pulled most of the changes back into his project (I am behind on issuing a pull request for NHibernate, sorry). This post is a follow-up on those additions with an updated conclusion, if you hadn't read the <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data" title="Evaluating ORMs for Batch Data Performance">previous post</a>, it's probably still worth a read.</p>

<h2>The Updated Lineup</h2>

<p>The updated lineup for tests now looks like:</p>

<ul>
<li>EntityFramework 4.1<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>AutoDetectChanges Disabled</li>
	<li>Tuned</li>
	</ul></li>
<li>EntityFramework 5.0 beta1<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>AutoDetectChanges Disabled</li>
	<li>Proxy Entities</li>
	<li>Tuned</li>
	</ul></li>
<li>Dapper 1.8<br />
	<ul>
	<li>Dapper Rainbow</li>
	<li>My Best effort at making it go fast</li>
	<li>Batching inserts/updates using transactions</li>
	</ul></li>
<li>LINQ to SQL<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>Tuned</li>
	</ul></li>
<li>NHibernate 3.2.0.4<br />
	<ul>
	<li>Basic implementation</li>
	<li>Stateless + 200 record batches</li>
	<li>Stateless + 1000 record batches</li>
	</ul></li>
<li>PetaPoco 3.04<br />
	<ul>
	<li>Basic implementation</li>
	<li>Batching inserts via transaction</li>
	</ul></li>
<li>Simple.Data 1.0.0 rc 0<br />
	<ul>
	<li>Basic implementation</li>
	<li>Batch inserts (built-in, automagical)</li>
	</ul></li>
<li>Raw ADO methods<br />
	<ul>
	<li>Basic SQL Command</li>
	<li>SQL Command w/ Transaction</li>
	<li>Bulk insert via SqlDataAdapter</li>
	<li>Bulk insert via SqlBulkCopy and SqlBulkCopy w/ Table Lock option</li>
	<li>Bulk insert via one really big SQL string (single insert w/ lots of concatenated value statements)</li>
	</ul></li>
</ul>

<p>Along the way I also updated the project to include more information in failing assertions (the state is asserted after each test is run).</p>

<h2>The Results</h2>
<p>I was simultaneously surprised and not surprised at the results. With Mark Rendle (<a href="http://blog.markrendle.net/" title="Mark's blog">blog</a>|<a href="https://twitter.com/markrendle" title="MarkRendle on twitter">twitter</a>) suggesting I add Simple.Data, I fully expected it to keep up with SqlBulkCopy, but it was still surprising to see the real, raw data.</p>

<div style="text-align: center; color: #666666; font-size: 90%">
	<img src="http://tiernok.com/LTDBlog/ORM/GraphB-1.png" alt="Graph of best bulk insert times for each major method" /><br />
		10,000 row inserts, Best times for each category
</div>

<p>Raw SqlBulkCopy was still the best option for 10,000 records (this is the TabLock option variant), but Simple.Data was right up there with it. PetaPoco and NHibernate are our other new additions and they were fairly close, about 20-25% slower than Dapper, or about 7x the SqlBulkCopy test.</p>

<p>But the important item on this graph really is Simple.Data. There are many that argue that all ORMs are slow and not worth spending time on but this is clearly not so. Simple.Data was a little slower than SqlBulkCopy and much faster than the other raw ADO variants. And it does this with no column mappings, no tricks and no special configurations. That is a huge milestone for ORMs and makes it tempting to use instead of SqlBulkCopy, as this method may actually be less fragile (no column mappings have to be manually defined).</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb17326'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb17326','cb29954'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb17326" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> BatchConfiguration<span style="color: #000000;">&#40;</span>IConnectionString connectionString<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; _db = Database.<span style="color: #0000FF;">OpenConnection</span><span style="color: #000000;">&#40;</span>connectionString.<span style="color: #0000FF;">FormattedConnectionString</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; _entitiesToInsert = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> List&lt;TestEntity&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Add<span style="color: #000000;">&#40;</span>TestEntity entity<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; _entitiesToInsert.<span style="color: #0000FF;">Add</span><span style="color: #000000;">&#40;</span>entity<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; </li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Commit<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; _db.<span style="color: #0000FF;">TestEntities</span>.<span style="color: #0000FF;">Insert</span><span style="color: #000000;">&#40;</span>_entitiesToInsert<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb29954" style="display: none; color: red;"></div></div></div>

<p>Create a connection, add as many items as you want to the list, then call Insert on the whole list. Magic.</p>

<h2>More Detail</h2>
<p>In the last post I included a comparison of all of the various methods used to insert data as a single graph. Unfortunately that graph was hard to read, as the basic Entity Framework methods were operating about an order of magnitude slower than everything else. I still thought the data was interesting, though, so I have created a graph with those values truncated as I did in the original post's followup. The one critical difference is that I am plotting the 10,000 record inserts instead of 100,000 like last time. That just took too long to run <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p>

<div style="text-align: center; color: #666666; font-size: 90%">
	<img src="http://tiernok.com/LTDBlog/ORM/GraphB-2.png" alt="10,000 row inserts, All categories and variants" /><br />
		10,000 row inserts, All categories and variants
</div>

<p>With more details we can see the spread and variation a little more clearly. </p>

<p><b>Big 'Ol String method:</b> One of the other surprises I ran into when doing this was how slow the "cram it all into one long string" method was ("SqlCommand - Insert Once" above). This is often suggested as a quick and dirty way to do batch insertion, basically by concatenating all the data into a single long INSERT statement with multiple VALUE rows or UNIONs. At 100 records this option tied with Dapper as the fastest method. Unfortunately it scales really horribly, moving up to nearly last, leading me to believe that people suggesting this method only tested it in lower ranges.</p>

<h2>Conclusions</h2>
<p>In my last post I concluded that I wouldn't use an ORM for batch processing, but Simple.Data has forced me to reconsider that statement. Raw speed is not the only measure I would use to select a tool for getting data A into database B, but it is clear that if my requirements include bulk data insertion, ORMs are no longer automatically taken off the table. Congratulations to Mark for making a very clean, very fast way to get a whole lot of data into a database for very little up front effort (and thanks for adding it to my todo list).</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/followup-on-orms-for-batch">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/followup-on-orms-for-batch#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1793</wfw:commentRss>
		</item>
				<item>
			<title>Reducing Code-Build-Test Friction with NCrunch</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/UnitTest/reducing-code-build-test-friction</link>
			<pubDate>Mon, 23 Jul 2012 10:13:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Unit Testing</category>			<guid isPermaLink="false">1773@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;As I&#039;ve moved from project to project, environment to environment, I&#039;ve had opportunities to write unit tests after coding, do test first development, and once use unit tests as a living spec for an external developer (code none unit testing?). One of the biggest friction points, once you settle on a framework, is the constant cycle back and forth between coding, building, running tests, and flipping back. Whether you are using MS Test and the built-in test result viewer, the external NUnit GUI, or a 3rd party test runner, that constant switching is actually stealing precious moments of concentration and time. &lt;/p&gt;

&lt;div style=&quot;text-align:center; color: #666666; font-size: 90%&quot;&gt;
   &lt;img src=&quot;http://tiernok.com/LTDBlog/AddressTDD/CodeBuildTest.png&quot; alt=&quot;The Code, Build, Test Cycle&quot; /&gt;&lt;br /&gt;
   The Code, Build, Test Cycle
&lt;/div&gt;

&lt;p&gt;Imagine for a moment that you have finished writing a piece of code. Maybe it&#039;s the test, maybe it&#039;s the code you intend to test. Instead of kicking off a build and switching mental mode to run the tests, the results simply start appearing. Your test lights up red, then green as you switch to building the logic that satisfies it, never once breaking stride to wait for the test suite to run. Your uncovered code is clearly marked as uncovered before you even finish writing it to press save. It&#039;s magic.&lt;/p&gt;

&lt;p&gt;And so very, very addictive.&lt;/p&gt;

&lt;h2&gt;Hey Kids, Try Some of This&lt;/h2&gt;

&lt;p&gt;Instead of screenshotting our way through another post, let&#039;s do this together. First download and install &lt;a href=&quot;http://www.ncrunch.net/&quot; title=&quot;Visit the NCrunch website&quot; target=&quot;_blank&quot;&gt;NCrunch&lt;/a&gt;. Then either download or clone the git repository sample I have set up here: &lt;a href=&quot;https://github.com/tarwn/TDDAddress&quot;&gt;https://github.com/tarwn/TDDAddress&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let&#039;s get started.&lt;/p&gt;

&lt;h2&gt;What are we doing?&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/AddressTDD/Letter.png&quot; alt=&quot;Letter&quot; style=&quot;float: left; margin-top: 8px;&quot; /&gt; &lt;/p&gt;

&lt;p&gt;The goal of this exercise is to build an Address class that is the business and formatting logic behind entry of a mailing address. The Address class will expose properties to tell an interface what address fields are available, what they should be labelled, whether the address has all the required values, and a displayable formatted address. In return, it will expect that interface to populate input properties for all the address input values. &lt;/p&gt;

&lt;p&gt;This is both as easy and quite a bit harder than what it sounds like. Easier in that we won&#039;t be writing a game or similarly large construct, but harder because the rules for mailing addresses are not as well defined or as simple as you may think. In fact, most websites on the internet do it wrong, and not just for addresses outside the US. &lt;/p&gt;

&lt;p&gt;Luckily there are some people that have tried to pull together all of the rules from the USPS and other sources and we can use the results of their hard work to serve as a spec for our Address logic. The rules we are using were sourced from &lt;a href=&quot;http://www.columbia.edu/~fdc/postal/#general&quot;&gt;http://www.columbia.edu/~fdc/postal/#general&lt;/a&gt;, but I&#039;ve only included a subset of them in this project.&lt;/p&gt;

&lt;h2&gt;Project Setup&lt;/h2&gt;

&lt;p&gt;There are two projects in the solution, one to hold the Address class (Main) and one for the tests (Main.Tests). &lt;/p&gt;

&lt;div style=&quot;text-align:center; color: #666666; font-size: 90%&quot;&gt;
   &lt;img src=&quot;http://tiernok.com/LTDBlog/AddressTDD/SolutionExplorer.png&quot; alt=&quot;Solution Explorer - Projects&quot; /&gt;&lt;br /&gt;
   Just a pair of small projects, nothing scary here
&lt;/div&gt;

&lt;p&gt;The Address class already has the basic properties it needs, but everything else is up to you.&lt;/p&gt;

&lt;h2&gt;Let&#039;s Go&lt;/h2&gt;

&lt;p&gt;Open the solution and enable NCrunch (it finished installing, right?) by selecting it from the top menu and selecting &quot;Enable&quot;. For the most part you can select the defaults when it is enabling. Either select the option to enable all of your tests by default while it is going through the dialogs, or open the Tests window, make ignored tests visible (grey icon on far right) and enable tests for the two projects manually.&lt;/p&gt;

&lt;p&gt;Ready? Ok, moving on.&lt;/p&gt;

&lt;p&gt;Open up the AddressTests file in Main.Tests and the Specs.md file from the root of the solution. Add the first test to the AddressTests like so:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb58135&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ToFormattedAddress_AddressLine1IsProvided_ItAppearsInOutput&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var sampleValue = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Address Line 1&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var a = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Address&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; a.&lt;span style=&quot;color: #0000FF;&quot;&gt;AddressLine1&lt;/span&gt; = sampleValue;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var result = a.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToFormattedAddress&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;result.&lt;span style=&quot;color: #0000FF;&quot;&gt;Contains&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;sampleValue&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb39058&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This test is the second rule in the included Specs.md file. After you add the test code, red dots will show up next to executable lines in the test that are on the path to a failed assertion. &lt;/p&gt;

&lt;div style=&quot;text-align:center; color: #666666; font-size: 90%&quot;&gt;
   &lt;img src=&quot;http://tiernok.com/LTDBlog/AddressTDD/TestsAreRed.png&quot; alt=&quot;Tests Are Red&quot; /&gt;&lt;br /&gt;
   Tests are automatically red/failing
&lt;/div&gt;

&lt;p&gt;NCrunch is building and running the tests behind the scenes as you add more code, automagically. Switching over to the Address class, we&#039;ll notice that it also has dots to indicate portions of the class that are referenced by failing tests. Right-clicking on any of these dots provides more details, options to run tests in debug mode, and so on.&lt;/p&gt;

&lt;p&gt;Now add some code to satisfy that test. As you make addition, NCrunch continues to build and test in the background, displaying the updated dots as you work. &lt;/p&gt;

&lt;div style=&quot;text-align:center; color: #666666; font-size: 90%&quot;&gt;
   &lt;img src=&quot;http://tiernok.com/LTDBlog/AddressTDD/TestsAreGreen.png&quot; alt=&quot;Tests Are Green&quot; /&gt;&lt;br /&gt;
   Tests and code turn Green automatically
&lt;/div&gt;

&lt;p&gt;When you get to all green dots, you&#039;re done. No need to stop, just move right on to the next test.&lt;/p&gt;

&lt;div class=&quot;videoblock&quot;&gt;&lt;object data=&quot;http://www.youtube.com/v/E0PztmQQlOQ&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/E0PztmQQlOQ&quot;&gt;&lt;/param&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot;&gt;&lt;/param&gt;&lt;/object&gt;&lt;/div&gt;

&lt;p&gt;Enjoy the flow.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/UnitTest/reducing-code-build-test-friction&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>As I've moved from project to project, environment to environment, I've had opportunities to write unit tests after coding, do test first development, and once use unit tests as a living spec for an external developer (code none unit testing?). One of the biggest friction points, once you settle on a framework, is the constant cycle back and forth between coding, building, running tests, and flipping back. Whether you are using MS Test and the built-in test result viewer, the external NUnit GUI, or a 3rd party test runner, that constant switching is actually stealing precious moments of concentration and time. </p>

<div style="text-align:center; color: #666666; font-size: 90%">
   <img src="http://tiernok.com/LTDBlog/AddressTDD/CodeBuildTest.png" alt="The Code, Build, Test Cycle" /><br />
   The Code, Build, Test Cycle
</div>

<p>Imagine for a moment that you have finished writing a piece of code. Maybe it's the test, maybe it's the code you intend to test. Instead of kicking off a build and switching mental mode to run the tests, the results simply start appearing. Your test lights up red, then green as you switch to building the logic that satisfies it, never once breaking stride to wait for the test suite to run. Your uncovered code is clearly marked as uncovered before you even finish writing it to press save. It's magic.</p>

<p>And so very, very addictive.</p>

<h2>Hey Kids, Try Some of This</h2>

<p>Instead of screenshotting our way through another post, let's do this together. First download and install <a href="http://www.ncrunch.net/" title="Visit the NCrunch website" target="_blank">NCrunch</a>. Then either download or clone the git repository sample I have set up here: <a href="https://github.com/tarwn/TDDAddress">https://github.com/tarwn/TDDAddress</a></p>

<p>Let's get started.</p>

<h2>What are we doing?</h2>

<p><img src="http://tiernok.com/LTDBlog/AddressTDD/Letter.png" alt="Letter" style="float: left; margin-top: 8px;" /> </p>

<p>The goal of this exercise is to build an Address class that is the business and formatting logic behind entry of a mailing address. The Address class will expose properties to tell an interface what address fields are available, what they should be labelled, whether the address has all the required values, and a displayable formatted address. In return, it will expect that interface to populate input properties for all the address input values. </p>

<p>This is both as easy and quite a bit harder than what it sounds like. Easier in that we won't be writing a game or similarly large construct, but harder because the rules for mailing addresses are not as well defined or as simple as you may think. In fact, most websites on the internet do it wrong, and not just for addresses outside the US. </p>

<p>Luckily there are some people that have tried to pull together all of the rules from the USPS and other sources and we can use the results of their hard work to serve as a spec for our Address logic. The rules we are using were sourced from <a href="http://www.columbia.edu/~fdc/postal/#general">http://www.columbia.edu/~fdc/postal/#general</a>, but I've only included a subset of them in this project.</p>

<h2>Project Setup</h2>

<p>There are two projects in the solution, one to hold the Address class (Main) and one for the tests (Main.Tests). </p>

<div style="text-align:center; color: #666666; font-size: 90%">
   <img src="http://tiernok.com/LTDBlog/AddressTDD/SolutionExplorer.png" alt="Solution Explorer - Projects" /><br />
   Just a pair of small projects, nothing scary here
</div>

<p>The Address class already has the basic properties it needs, but everything else is up to you.</p>

<h2>Let's Go</h2>

<p>Open the solution and enable NCrunch (it finished installing, right?) by selecting it from the top menu and selecting "Enable". For the most part you can select the defaults when it is enabling. Either select the option to enable all of your tests by default while it is going through the dialogs, or open the Tests window, make ignored tests visible (grey icon on far right) and enable tests for the two projects manually.</p>

<p>Ready? Ok, moving on.</p>

<p>Open up the AddressTests file in Main.Tests and the Specs.md file from the root of the solution. Add the first test to the AddressTests like so:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb10093'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb10093','cb98309'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb10093" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ToFormattedAddress_AddressLine1IsProvided_ItAppearsInOutput<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var sampleValue = <span style="color: #808080;">&quot;Address Line 1&quot;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var a = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Address<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; a.<span style="color: #0000FF;">AddressLine1</span> = sampleValue;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; var result = a.<span style="color: #0000FF;">ToFormattedAddress</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>result.<span style="color: #0000FF;">Contains</span><span style="color: #000000;">&#40;</span>sampleValue<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li></ol></div><div id="cb98309" style="display: none; color: red;"></div></div></div>

<p>This test is the second rule in the included Specs.md file. After you add the test code, red dots will show up next to executable lines in the test that are on the path to a failed assertion. </p>

<div style="text-align:center; color: #666666; font-size: 90%">
   <img src="http://tiernok.com/LTDBlog/AddressTDD/TestsAreRed.png" alt="Tests Are Red" /><br />
   Tests are automatically red/failing
</div>

<p>NCrunch is building and running the tests behind the scenes as you add more code, automagically. Switching over to the Address class, we'll notice that it also has dots to indicate portions of the class that are referenced by failing tests. Right-clicking on any of these dots provides more details, options to run tests in debug mode, and so on.</p>

<p>Now add some code to satisfy that test. As you make addition, NCrunch continues to build and test in the background, displaying the updated dots as you work. </p>

<div style="text-align:center; color: #666666; font-size: 90%">
   <img src="http://tiernok.com/LTDBlog/AddressTDD/TestsAreGreen.png" alt="Tests Are Green" /><br />
   Tests and code turn Green automatically
</div>

<p>When you get to all green dots, you're done. No need to stop, just move right on to the next test.</p>

<div class="videoblock"><object data="http://www.youtube.com/v/E0PztmQQlOQ" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"><param name="movie" value="http://www.youtube.com/v/E0PztmQQlOQ"></param><param name="wmode" value="transparent"></param></object></div>

<p>Enjoy the flow.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/UnitTest/reducing-code-build-test-friction">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/UnitTest/reducing-code-build-test-friction#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1773</wfw:commentRss>
		</item>
				<item>
			<title>Evaluating ORMs for Batch Data Performance</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data</link>
			<pubDate>Tue, 10 Jul 2012 10:32:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">ORM</category>			<guid isPermaLink="false">1767@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Earlier this week I came upon a post (&lt;a href=&quot;http://blog.staticvoid.co.nz/2012/03/entity-framework-comparative.html&quot; title=&quot;Entity Framework Comparative Performance at static void; blog&quot;&gt;Entity Framework Comparative Performance&lt;/a&gt;) by Luke McGregor (&lt;a href=&quot;http://blog.staticvoid.co.nz/&quot; title=&quot;static void; blog&quot;&gt;b&lt;/a&gt;|&lt;a href=&quot;https://twitter.com/staticv0id&quot; title=&quot;staticv0id on twitter&quot;&gt;t&lt;/a&gt;) that compared the performance of several ORMs for handling batch data. Given the amount of batch data I&#039;ve processed, I was curious how those ORM tests would line up against a couple common non-ORM methods.&lt;/p&gt;

&lt;p&gt;I decided to stick to ADO.Net methods for data and to focus on the insert, as a fast insert can be used to replace updates and deletes. SSIS and bcp would be alternative options, but would require additional setup to test alongside the .Net code.&lt;/p&gt;

&lt;div style=&quot;background-color: #dddddd; font-style: italic; padding: .5em; margin: bottom: .5em&quot;&gt;2012-07-11: In response to a comment below, I added a followup section to the post with a graph showing all the test results in the 100,000 record tests. &lt;/div&gt;

&lt;h2&gt;Method&lt;/h2&gt;
&lt;p&gt;The method for these tests closely resembles the one Luke followed in his original post. The main exception is that I am using a local SQL Server 2008 R2 instance rather than a remote VM. Unfortunately attempts to run a full test set against my remote SQL Server VM ran into errors each time I tried, generally in the Entity Framework setup, teardown, and assertion code. My local system has a large enough amount of RAM and cores that any impact from running the tests locally should be limited, with only the network constraint removed from the equation.&lt;/p&gt;

&lt;h2&gt;Tests&lt;/h2&gt;
&lt;p&gt;I focused entirely on the insert tests, adding two new tests to use SqlDataAdapter and SqlBulkCopy. The test lineup then became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EntityFramework 4.1&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;AutoDetectChanges Disabled&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;EntityFramework 5.0 beta1&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;AutoDetectChanges Disabled&lt;/li&gt;
	&lt;li&gt;Proxy Entities&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Dapper 1.8&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Dapper Rainbow&lt;/li&gt;
	&lt;li&gt;My Best effort at making it go fast&lt;/li&gt;
	&lt;li&gt;EDIT Batching inserts/updates using transactions&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;LINQ to SQL&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic Configuration (No optimizations)&lt;/li&gt;
	&lt;li&gt;Tuned&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Raw ADO methods&lt;br /&gt;
	&lt;ul&gt;
	&lt;li&gt;Basic SQL Command&lt;/li&gt;
	&lt;li&gt;SQL Command w/ Transaction&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/tarwn/StaticVoid.OrmPerformance/blob/master/Harness.SqlCommand/InsertViaDataAdapterConfiguration.cs&quot; title=&quot;Code for the SqlDataAdapter Scenario&quot;&gt;SqlDataAdapter&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com/tarwn/StaticVoid.OrmPerformance/blob/master/Harness.SqlCommand/InsertSqlBulkConfiguration.cs&quot; title=&quot;Code for the SqlBulkCopy method&quot;&gt;SqlBulkCopy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;&lt;i&gt;The raw code is available on my branch at github: &lt;a href=&quot;https://github.com/tarwn/StaticVoid.OrmPerformance&quot;&gt;https://github.com/tarwn/StaticVoid.OrmPerformance&lt;/a&gt; and the original is located on Luke&#039;s here: &lt;a href=&quot;https://github.com/lukemcgregor/StaticVoid.OrmPerformance&quot;&gt;https://github.com/lukemcgregor/StaticVoid.OrmPerformance&lt;/a&gt;.&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;SqlDataAdapter Method&lt;/h3&gt;
&lt;p&gt;The SqlDataAdapter method is to create a local DataSet or DataTable then provide this to a SqlDataAdapter with a configured SqlCommand object and parameters. The SqlDataAdapter takes care of the details, getting all the individual rows of data into the database via that insert command. Because we are operating on the full set of data, the test stores all of the values in memory until it is told to commit them.&lt;/p&gt;

&lt;h3&gt;SqlBulkCopy Method&lt;/h3&gt;
&lt;p&gt;The SqlBulkCopy object is designed to &quot;let you efficiently bulk load a SQL Server table&quot; (&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx&quot; title=&quot;MSDN - SQLBulkCopy&quot;&gt;MSDN&lt;/a&gt;). It provides similar functionality as bcp, but from inside our .Net code and without the format files. This test works similarly to the SqlDataAdapter test, in that it holds the entire DataTable of test data in memory until it is told to commit it in one go (some of the tests are iterative, some are bulk, the test method caters to both).&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;I&#039;ll admit the results were not that surprising. SqlBulkCopy was the fastest method for inserting larger amounts of data, but had some initial overhead that made it slower for the 1, 10, and 100 record tests. Compared to the best times from the other methods (SqlBulkCopy is the SqlCommand representative in the chart), the performance difference is clear:&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/Graph-1.png&quot; alt=&quot;Graph of Best Times for 1-10000 records&quot; /&gt;&lt;br /&gt;
Best Times for 1, 10, 100, 1000, 10000 scenarios
&lt;/div&gt;

&lt;p&gt;Extending this to a larger set of 100000 records and the difference is relatively the same. Relative to the prior set of results, SqlBulkCopy is not as much faster on the 100,000 run as it was on the 10,000. It would be interesting to switch to increments of 10,000 and see if there is a pattern to it.&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/Graph-2.png&quot; alt=&quot;Graph of Best Times for 1-100000 records&quot; /&gt;&lt;br /&gt;
Best Times for 1, 10, 100, 1000, 10000, 100000 scenarios
&lt;/div&gt;

&lt;p&gt;I also thought it was interesting to see how well the tuning improved some of the ORM methods. In the case of Entity Framework, it&#039;s clear that if you intend to use it for batch data then tuning is a requirement, not an option. The out-of-the-box experience for Entity Framework 4.1 and 5 were roughly an order of magnitude slower than all other tests.&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/Graph-3.png&quot; alt=&quot;Scaled out to show EF 4.1 and 5 Basic Performance&quot; /&gt;&lt;br /&gt;
Scaled out to show EF 4.1 and 5 Basic Performance
&lt;/div&gt;

&lt;p&gt;The other key indicator is memory. Our two new methods store all the data and send it in a single command, so they will have a higher memory footprint to accommodate that data. The methods that incrementally send the data, like the Dapper scenarios and basic SqlCommand option, will use very little data since they are flushing each addition directly to SQL.&lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/Graph-4.png&quot; alt=&quot;Memory Usage per Test Type&quot; /&gt;&lt;br /&gt;
Memory Usage per Test Type
&lt;/div&gt;

&lt;p&gt;This graph shows the memory/record of the 1000, 10000, and 100000 test runs. As we would expect, the memory/record for the full batch methods is reduced as the overhead is spread across more records. Entity Framework show consistently high memory usage, but the Proxy Entities method does bring it down to just about twice as much as Linq2SQL, which is in turn about 50% higher than the SqlCommand/SqlBulkCopy methods.&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;For pure, batch insertions, I still wouldn&#039;t use an ORM. These tests show that the ORMs tested are still significantly slower at batch insertion than tools built specifically for bulk operations, like SqlBulkCopy. We&#039;ve also seen that when we do use an ORM, understanding it&#039;s performance characteristics and how to tune the ORM can make an enormous difference in how well or poorly it works.&lt;/p&gt;

&lt;h2&gt;Follow-up&lt;/h2&gt;
&lt;p&gt;Based on Tudor&#039;s comment below, I&#039;ve generated a graph of the times for each method in the 100,000 record tests. Unlike the full chart above, I&#039;ve scaled it to ignore the two basic Entity Framework entries that cause the line chart above to be so unreadable. &lt;/p&gt;

&lt;div style=&quot;color: #666666; text-align: center; font-size: 90%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/ORM/Graph-Followup.png&quot; alt=&quot;Readable Execution Times for 1000,000 records&quot; /&gt;&lt;br /&gt;
Readable Execution Times for 1000,000 records
&lt;/div&gt;

&lt;p&gt;Using ADO.Net does not automatically mean better performance than an ORM. SqlBulkCopy does clearly perform better, but using a SqlCommand.ExecuteNonQuery or a SqlAdapter.InsertCommand does not achieve the same level of performance. Many of the ORM tests kept up or outperformed the non-transactional SqlCommand and SqlAdapter tests, and Dapper kept up with the Transactional SqlCommand test. ADO.Net itself is not giving the boost in speed we see from SqlBulkCopy, it&#039;s the use of a tool that is built specifically for batch processing (all of the rest operate at the row level, ADO.Net or ORM).&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Earlier this week I came upon a post (<a href="http://blog.staticvoid.co.nz/2012/03/entity-framework-comparative.html" title="Entity Framework Comparative Performance at static void; blog">Entity Framework Comparative Performance</a>) by Luke McGregor (<a href="http://blog.staticvoid.co.nz/" title="static void; blog">b</a>|<a href="https://twitter.com/staticv0id" title="staticv0id on twitter">t</a>) that compared the performance of several ORMs for handling batch data. Given the amount of batch data I've processed, I was curious how those ORM tests would line up against a couple common non-ORM methods.</p>

<p>I decided to stick to ADO.Net methods for data and to focus on the insert, as a fast insert can be used to replace updates and deletes. SSIS and bcp would be alternative options, but would require additional setup to test alongside the .Net code.</p>

<div style="background-color: #dddddd; font-style: italic; padding: .5em; margin: bottom: .5em">2012-07-11: In response to a comment below, I added a followup section to the post with a graph showing all the test results in the 100,000 record tests. </div>

<h2>Method</h2>
<p>The method for these tests closely resembles the one Luke followed in his original post. The main exception is that I am using a local SQL Server 2008 R2 instance rather than a remote VM. Unfortunately attempts to run a full test set against my remote SQL Server VM ran into errors each time I tried, generally in the Entity Framework setup, teardown, and assertion code. My local system has a large enough amount of RAM and cores that any impact from running the tests locally should be limited, with only the network constraint removed from the equation.</p>

<h2>Tests</h2>
<p>I focused entirely on the insert tests, adding two new tests to use SqlDataAdapter and SqlBulkCopy. The test lineup then became:</p>

<ul>
<li>EntityFramework 4.1<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>AutoDetectChanges Disabled</li>
	<li>Tuned</li>
	</ul></li>
<li>EntityFramework 5.0 beta1<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>AutoDetectChanges Disabled</li>
	<li>Proxy Entities</li>
	<li>Tuned</li>
	</ul></li>
<li>Dapper 1.8<br />
	<ul>
	<li>Dapper Rainbow</li>
	<li>My Best effort at making it go fast</li>
	<li>EDIT Batching inserts/updates using transactions</li>
	</ul></li>
<li>LINQ to SQL<br />
	<ul>
	<li>Basic Configuration (No optimizations)</li>
	<li>Tuned</li>
	</ul></li>
<li>Raw ADO methods<br />
	<ul>
	<li>Basic SQL Command</li>
	<li>SQL Command w/ Transaction</li>
	<li><a href="https://github.com/tarwn/StaticVoid.OrmPerformance/blob/master/Harness.SqlCommand/InsertViaDataAdapterConfiguration.cs" title="Code for the SqlDataAdapter Scenario">SqlDataAdapter</a></li>
	<li><a href="https://github.com/tarwn/StaticVoid.OrmPerformance/blob/master/Harness.SqlCommand/InsertSqlBulkConfiguration.cs" title="Code for the SqlBulkCopy method">SqlBulkCopy</a></li>
</ul></li></ul>

<p><i>The raw code is available on my branch at github: <a href="https://github.com/tarwn/StaticVoid.OrmPerformance">https://github.com/tarwn/StaticVoid.OrmPerformance</a> and the original is located on Luke's here: <a href="https://github.com/lukemcgregor/StaticVoid.OrmPerformance">https://github.com/lukemcgregor/StaticVoid.OrmPerformance</a>.</i></p>

<h3>SqlDataAdapter Method</h3>
<p>The SqlDataAdapter method is to create a local DataSet or DataTable then provide this to a SqlDataAdapter with a configured SqlCommand object and parameters. The SqlDataAdapter takes care of the details, getting all the individual rows of data into the database via that insert command. Because we are operating on the full set of data, the test stores all of the values in memory until it is told to commit them.</p>

<h3>SqlBulkCopy Method</h3>
<p>The SqlBulkCopy object is designed to "let you efficiently bulk load a SQL Server table" (<a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx" title="MSDN - SQLBulkCopy">MSDN</a>). It provides similar functionality as bcp, but from inside our .Net code and without the format files. This test works similarly to the SqlDataAdapter test, in that it holds the entire DataTable of test data in memory until it is told to commit it in one go (some of the tests are iterative, some are bulk, the test method caters to both).</p>

<h2>Results</h2>
<p>I'll admit the results were not that surprising. SqlBulkCopy was the fastest method for inserting larger amounts of data, but had some initial overhead that made it slower for the 1, 10, and 100 record tests. Compared to the best times from the other methods (SqlBulkCopy is the SqlCommand representative in the chart), the performance difference is clear:</p>

<div style="color: #666666; text-align: center; font-size: 90%">
<img src="http://tiernok.com/LTDBlog/ORM/Graph-1.png" alt="Graph of Best Times for 1-10000 records" /><br />
Best Times for 1, 10, 100, 1000, 10000 scenarios
</div>

<p>Extending this to a larger set of 100000 records and the difference is relatively the same. Relative to the prior set of results, SqlBulkCopy is not as much faster on the 100,000 run as it was on the 10,000. It would be interesting to switch to increments of 10,000 and see if there is a pattern to it.</p>

<div style="color: #666666; text-align: center; font-size: 90%">
<img src="http://tiernok.com/LTDBlog/ORM/Graph-2.png" alt="Graph of Best Times for 1-100000 records" /><br />
Best Times for 1, 10, 100, 1000, 10000, 100000 scenarios
</div>

<p>I also thought it was interesting to see how well the tuning improved some of the ORM methods. In the case of Entity Framework, it's clear that if you intend to use it for batch data then tuning is a requirement, not an option. The out-of-the-box experience for Entity Framework 4.1 and 5 were roughly an order of magnitude slower than all other tests.</p>

<div style="color: #666666; text-align: center; font-size: 90%">
<img src="http://tiernok.com/LTDBlog/ORM/Graph-3.png" alt="Scaled out to show EF 4.1 and 5 Basic Performance" /><br />
Scaled out to show EF 4.1 and 5 Basic Performance
</div>

<p>The other key indicator is memory. Our two new methods store all the data and send it in a single command, so they will have a higher memory footprint to accommodate that data. The methods that incrementally send the data, like the Dapper scenarios and basic SqlCommand option, will use very little data since they are flushing each addition directly to SQL.</p>

<div style="color: #666666; text-align: center; font-size: 90%">
<img src="http://tiernok.com/LTDBlog/ORM/Graph-4.png" alt="Memory Usage per Test Type" /><br />
Memory Usage per Test Type
</div>

<p>This graph shows the memory/record of the 1000, 10000, and 100000 test runs. As we would expect, the memory/record for the full batch methods is reduced as the overhead is spread across more records. Entity Framework show consistently high memory usage, but the Proxy Entities method does bring it down to just about twice as much as Linq2SQL, which is in turn about 50% higher than the SqlCommand/SqlBulkCopy methods.</p>

<h2>Conclusions</h2>
<p>For pure, batch insertions, I still wouldn't use an ORM. These tests show that the ORMs tested are still significantly slower at batch insertion than tools built specifically for bulk operations, like SqlBulkCopy. We've also seen that when we do use an ORM, understanding it's performance characteristics and how to tune the ORM can make an enormous difference in how well or poorly it works.</p>

<h2>Follow-up</h2>
<p>Based on Tudor's comment below, I've generated a graph of the times for each method in the 100,000 record tests. Unlike the full chart above, I've scaled it to ignore the two basic Entity Framework entries that cause the line chart above to be so unreadable. </p>

<div style="color: #666666; text-align: center; font-size: 90%">
<img src="http://tiernok.com/LTDBlog/ORM/Graph-Followup.png" alt="Readable Execution Times for 1000,000 records" /><br />
Readable Execution Times for 1000,000 records
</div>

<p>Using ADO.Net does not automatically mean better performance than an ORM. SqlBulkCopy does clearly perform better, but using a SqlCommand.ExecuteNonQuery or a SqlAdapter.InsertCommand does not achieve the same level of performance. Many of the ORM tests kept up or outperformed the non-transactional SqlCommand and SqlAdapter tests, and Dapper kept up with the Transactional SqlCommand test. ADO.Net itself is not giving the boost in speed we see from SqlBulkCopy, it's the use of a tool that is built specifically for batch processing (all of the rest operate at the row level, ADO.Net or ORM).</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/ORM/evaluating-orms-for-batch-data#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1767</wfw:commentRss>
		</item>
				<item>
			<title>Monitoring and Logging as a Service - Reviews</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews</link>
			<pubDate>Thu, 05 Jul 2012 13:43:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Instrumentation</category>			<guid isPermaLink="false">1749@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;There is a lot of value in knowing what the internals of your application are doing and, more importantly, knowing them 5 minutes ago before someone called to complain the system is slow. Instrumenting an application sounds like a complex task, but in &lt;a href=&quot;http://blogs.lessthandot.com/index.php/All/?p=1748&quot; title=&quot;Monitoring and Logging as a Service - The Common Bits&quot;&gt;my prior post&lt;/a&gt; I showed some sample code that allowed me to tie into several log services to store data from my system as it is running. &lt;/p&gt;

&lt;p&gt;Keep in mind, I have extremely high hopes for these types of services, due to my experience in instrumentation and logging in manufacturing environments (&lt;a href=&quot;http://blogs.lessthandot.com/index.php/All/?p=1747&quot; title=&quot;Monitoring and Logging as a Service - Introduction&quot;&gt;see the first post&lt;/a&gt;). None of these systems are built with this task specifically in mind, so services that are absolutely wonderful at their core competencies may not prove to be a good fit for this specific use case.&lt;/p&gt;

&lt;p&gt;In any case, let&#039;s see what we&#039;re going to be looking at:&lt;/p&gt;
&lt;li&gt;&lt;a href=&quot;http://www.loggly.com/&quot; title=&quot;Visit Loggly&quot; target=&quot;_blank&quot;&gt;Loggly&lt;/a&gt; - &quot;It&#039;s fast, fun and easy to use&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.datadoghq.com/&quot; title=&quot;Visit DataDog&quot; target=&quot;_blank&quot;&gt;DataDog&lt;/a&gt; - &quot;On a mission to bring sanity to IT Management&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.splunkstorm.com/&quot; title=&quot;Visit Splunk Storm&quot; target=&quot;_blank&quot;&gt;Splunk Storm&lt;/a&gt; - &quot;Your data has the answers, we help you find them.&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.sumologic.com/&quot; title=&quot;Visit Sumologic&quot; target=&quot;_blank&quot;&gt;Sumo  Logic&lt;/a&gt; - &quot;Make Your Applications Run Longer &amp;amp; Stronger&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.logentries.com/&quot; title=&quot;Visit logentries&quot; target=&quot;_blank&quot;&gt;logentries&lt;/a&gt; - &quot;We make your life easier&quot;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://papertrailapp.com/&quot; title=&quot;Visit papertrail&quot; target=&quot;_blank&quot;&gt;papertrail&lt;/a&gt; - &quot;Get back to work.&quot;&lt;/li&gt;

&lt;p&gt;A few of these were non-starters, but I&#039;ll record what I know about them. The rest I&#039;ll present in similar formats for comparison.&lt;/p&gt;

&lt;h2&gt;Loggly&lt;/h2&gt;
&lt;p&gt;Loggly was one of the first logging services on the market as well as one of the first I instrumented with the application. &lt;/p&gt;

&lt;h3&gt;Highlights:&lt;/h3&gt;
&lt;p&gt;Loggly supports raw text, syslog, and JSON data posts and is a live service (not beta).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Input Support: &lt;/b&gt; HTTP with raw text or JSON plus an array of syslog options&lt;br /&gt;
&lt;b&gt;Ease of Setup: &lt;/b&gt; No default inputs, have to create an initial HTTP JSON input which is fairly easy. Good samples provided for communicating with input.&lt;br /&gt;
&lt;b&gt;Searching:&lt;/b&gt; The search uses a console-like command line input for searches with limited direction and assistance (more later), I didn&#039;t invest the time that would have been necessary to learn enough of the search interface to become even a beginning user&lt;br /&gt;
&lt;b&gt;Visualization: &lt;/b&gt; Nonexistent. Has examples of how to build your own in python, jQuery, and google charts&lt;/p&gt;

&lt;h3&gt;General Opinion:&lt;/h3&gt;
&lt;p&gt;I would not use this service for application instrumentation. The search is the only feature it offers and that is in beta (Loggly has been live for several years). The command-line search idea is cute, but to me is an example of form over function that suffers from sluggish response times, low usability, and an unnecessary initial learning curve. My use of the site was characterized by extremely long loading times (30-60 seconds) and the lack of initial defaults for new users seems like a missed opportunity. If they were to focus on fixing the performance issues, improving the usability, and implementing visualization, I&#039;d be ready to give them a second look.&lt;/p&gt;

&lt;h3&gt;Running the Sample Site w/ Loggly&lt;/h3&gt;
&lt;p&gt;Clone a copy of the sample application from &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode&quot; title=&quot;InstrumentationSampleCode on GitHub&quot;&gt;tarwn/InstrumentationSampleCode on github&lt;/a&gt;. Go to the &lt;a href=&quot;http://www.loggly.com&quot;&gt;Loggly.com website&lt;/a&gt;, sign up for an account (you can get a free account by dragging the bars on the pricing page to the lowest values), create an HTTP input with JSON. &lt;/p&gt;

&lt;p&gt;Once you have the input created, create or edit the &lt;i&gt;sensitive.config&lt;/i&gt; file at the root of the solution with the following values (substitute your GUID in where appropriate):&lt;/p&gt;

&lt;p&gt;&lt;b&gt;sensitive.config&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb41093&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;LogProvider = Loggly&lt;br /&gt;Loggly.BaseURL = &amp;lt;a href=&amp;quot;https://logs.loggly.com/inputs/put-your-guid-here&amp;quot;&amp;gt;https://logs.loggly.com/inputs/put-your-guid-here&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb45303&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the site, try a few things, then switch over to the Loggly screen and see if a page will load long enough to show it received some data.&lt;/p&gt;

&lt;h2&gt;DataDog (Could Not Review)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.datadoghq.com/&quot; title=&quot;Visit DataDogHQ&quot;&gt;DataDog&lt;/a&gt; looked like it would be a good option, but after setting up my initial account I found I could not continue until I installed an agent on a server with a supported operating system. Unfortunately Windows is not supported (which was not obvious until you got signed up), so I was unable to continue. &lt;/p&gt;

&lt;p&gt;They did email me a few days later to ask if I had any difficulties getting signed up since I hadn&#039;t finished the activation step, and the email looked personal and friendly, so I may return and see if they can help me get past that step to try the service for instrumentation purposes. &lt;/p&gt;

&lt;h2&gt;Splunk Storm&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.splunkstorm.com/&quot; title=&quot;Visit Splunk Storm&quot; target=&quot;_blank&quot;&gt;Splunk Storm&lt;/a&gt; is a beta service from Splunk, which is a name that should be highly recognizable in the logging world. Storm is their beta service to offer the capabilities of Splunk without a local installation.&lt;/p&gt;

&lt;h3&gt;Highlights: &lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Input Support: &lt;/b&gt; HTTP with raw text, syslog, netcat, and &quot;any other application that transmits via TCP or UDP&quot;&lt;br /&gt;
&lt;b&gt;Ease of Setup: &lt;/b&gt; Easy enough. API tokens are already available for use but error reporting on individual HTTP posts can sometimes be useless (501s for instance)&lt;br /&gt;
&lt;b&gt;Searching:&lt;/b&gt; The search for splunk is the bar to measure the others by. They use a text-based language but everything on the screen is clickable, common fields are captured and listed on the side for easy searching, and the search language itself is something you can pick up a bit at a time&lt;br /&gt;
&lt;b&gt;Visualization: &lt;/b&gt; Pretty decent but there are some definite learning curve moments and when you try to graph too much data it throws away parts of the dataset entirely&lt;/p&gt;

&lt;h3&gt;General Opinion:&lt;/h3&gt;
&lt;p&gt;I have been using Splunk Storm for longer than any of the other services. The service itself is very promising. It offers a really good search capability with a great drilldown capability and setup was easy. Often it will give mini-help boxes when typing terms into the search and suggest similar searches I have done recently, details on commands, and suggestions for commands I may want to use.&lt;/p&gt;

&lt;p&gt;Unfortunately they are using the bad definition of &quot;beta&quot;, instead of the &quot;it&#039;s relatively stable but not feature complete&quot; definition. There have been at least two occurrences where I was locked out of the system while they performed maintenance, on one occasion an update actually locked me out of the main account for 4-5 days (with a constant friendly reminder that service would be back momentarily). Documentation links are present throughout the application but most of them lead to 404 pages. The fact that charts throw away portions of the dataset when you ask them to chart too much can be incredibly frustrating (when you figure out what it&#039;s doing). &lt;/p&gt;

&lt;p&gt;After the initial exposure I started to find that I was spending more and more time in the splunk documentation (once you figure out how to get there) simply trying to learn their command-line language because the interface only gets you so far. The power of the tool is impressive, but the level of effort I would have to spend learning how to use their tool is a common annoyance that reminds me of Loggly&#039;s command-line tool. I&#039;ve also seen more of their 404 narwhal page then I ever expected to see, as apparently any page that is left open for too long refreshes and drops you there (session time out perhaps?).&lt;/p&gt;

&lt;h3&gt;Running the Sample Site w/ Splunk Storm:&lt;/h3&gt;
&lt;p&gt;Clone a copy of the sample application from &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode&quot; title=&quot;InstrumentationSampleCode on GitHub&quot;&gt;tarwn/InstrumentationSampleCode on github&lt;/a&gt;. Go to the &lt;a href=&quot;http://www.splunkstorm.com&quot; target=&quot;_blank&quot;&gt;splunkstorm.com website&lt;/a&gt; and sign up for an account (all accounts are free while it is in beta).&lt;/p&gt;

&lt;p&gt;In the inputs section of the project there is a tab called &quot;API&quot; that has the access token and project id. Add or edit the &lt;i&gt;sensitive.config&lt;/i&gt; file at the root of the .Net solution, substituting your values where appropriate:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;sensitive.config&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb69596&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;LogProvider = storm&lt;br /&gt;&amp;nbsp;&lt;br /&gt;Storm.AccessToken = your-access-token-here&lt;br /&gt;Storm.BaseURL = &amp;lt;a href=&amp;quot;https://api.splunkstorm.com/1/inputs/http&amp;quot;&amp;gt;https://api.splunkstorm.com/1/inputs/http&amp;lt;/a&amp;gt;&lt;br /&gt;Storm.ProjectId = your-project-id-here&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb5884&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Load up the sample app, click some buttons, then switch over to Splunk Storm and try out the search. Clicking different items in the log entries will fire off searches. Unfortunately charting is almost a blog series on it&#039;s own, sorry.&lt;/p&gt;

&lt;h2&gt;Sumo Logic (Could Not Review)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.sumologic.com&quot; title=&quot;Visit Sumo Logic&quot; target=&quot;_blank&quot;&gt;Sumo Logic&lt;/a&gt; claims to provide &quot;the first cloud-based log management and analytics solution&quot;. The demo video shows a product that has similar capabilities to Splunk in the search area with additions of automatic pattern detection and monitoring. Initially I did not demo the product because they don&#039;t offer unattended demos, you have to sign up and wait for someone to contact you to hold your hand while you demo their service. Later, as I was reading their FAQ, I found that they do not offer an API, accepting data only via collectors that are installed on your local system. The collector idea isn&#039;t bad, manufacturing historians have been doing this for a couple decades after all, but without demoing I couldn&#039;t tell how easy or hard it would be to install them and automate that installation for those of us with large numbers of servers or elastic infrastructure.&lt;/p&gt;

&lt;h2&gt;logentries&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://logentries.com//&quot; title=&quot;Visit logentries&quot; target=&quot;_blank&quot;&gt;logentries&lt;/a&gt; is a cloud logging service that has been on the market for over a year and offers free and metered accounts.&lt;/p&gt;

&lt;h3&gt;Highlights: &lt;/h3&gt;
&lt;p&gt;&lt;b&gt;Input Support: &lt;/b&gt; HTTP with raw text, agents, or an array of syslog options&lt;br /&gt;
&lt;b&gt;Ease of Setup: &lt;/b&gt; No default inputs, have to create an initial log. Doesn&#039;t respond to success or error HTTP calls, making troubleshooting difficult. Initial data didn&#039;t show in site until I specifically hit the refresh button in the top right of the app, leading to several hours of frustrated and unnecessary troubleshooting initially.&lt;br /&gt;
&lt;b&gt;Searching:&lt;/b&gt; The search seems fairly basic, but they pattern matching and tagging ability lends a second dimension to search capabilities. Creating patterns was not straightforward, however.&lt;br /&gt;
&lt;b&gt;Visualization: &lt;/b&gt; Graphs are purely around tags and data records, no ability to extract values from entries and graph them (so somewhere between loggly and splunk in this regard)&lt;/p&gt;

&lt;h3&gt;General Opinion:&lt;/h3&gt;
&lt;p&gt;Logentries doesn&#039;t seem like a good solution for application instrumentation, but this is more due to their focus being different from what I was looking for. I did have difficulties with the initial setup due to the lack of response from their HTTP service, the fact that all of their language examples use raw TCP sockets instead of HTTP libraries, and the delays in data being sent and actually showing up in the interface.&lt;/p&gt;

&lt;p&gt; I also found the search and pattern sections frustrating. That being said, some of this was due to me trying to misuse their service for instrumentation and because their documentation is not linked from inside the interface. The search supports regular expressions and provides a good number of examples, which makes it almost as powerful as the other services without the need to learn a whole new syntax or language. &lt;/p&gt;

&lt;p&gt;With one exception, if I was looking for a log monitoring and alerting system I would consider starting here because I could get farther for a reduced learning curve and startup time.  Unfortunately the exception is that I can&#039;t tell if my data is actually reaching them or not. Switching to the TCP method they outline may or may not resolve this, but the lack of error feedback even if I got it working with that method.&lt;/p&gt;

&lt;h3&gt;Running the Sample Site w/ logentries:&lt;/h3&gt;
&lt;p&gt;Clone a copy of the sample application from &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode&quot; title=&quot;InstrumentationSampleCode on GitHub&quot;&gt;tarwn/InstrumentationSampleCode on github&lt;/a&gt;. Go to the &lt;a href=&quot;http://www.logentries.com&quot; target=&quot;_blank&quot;&gt;logentries.com website&lt;/a&gt; and sign up for an account (there is a free account with limited storage).&lt;/p&gt;

&lt;p&gt;Add a new Host entry, then inside that host add a new Log with a type of &quot;API Library/HTML&quot;. Add or edit the &lt;i&gt;sensitive.config&lt;/i&gt; file at the root of the .Net solution, substituting your values where appropriate:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;sensitive.config&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb62517&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;LogProvider = logentries&lt;br /&gt;&amp;nbsp;&lt;br /&gt;logentries.BaseURL = &amp;lt;a href=&amp;quot;http://api.logentries.com&amp;quot;&amp;gt;http://api.logentries.com&amp;lt;/a&amp;gt;&lt;br /&gt;logentries.AccountKey = your-account-key-here&lt;br /&gt;logentries.Host = your-host-name&lt;br /&gt;logentries.Log = your-log-name-or-key&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb80427&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: I fired up the sample app pointed at logentries when I started writing this up, my data still hasn&#039;t shown up. I&#039;m not sure if it will or not.&lt;/p&gt;

&lt;h2&gt;Papertrail (Could Not Review)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://papertrailapp.com/&quot; title=&quot;Visit papertrail&quot; target=&quot;_blank&quot;&gt;Papertrail&lt;/a&gt; looks like a pretty solid service, from their front page. Unfortunately while they do support a large number of systems as log inputs, a REST API is not one of them. They do support syslog as an input source, however, so it should be possible to communicate from an instrumented app over TCP using the syslog format.After digging into more of their documentation, however, it looks like this system could offer some instrumentation support from an alerting perspective, but doesn&#039;t really offer the types of capabilities we would want for instrumentation (aggregating and analyzing values in like log entries, for instance API executing times across multiple calls). &lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Most instrumentation data options require local installers, many based on RRD storage or other, similar methods. I did find an open source server called &lt;a href=&quot;http://www.nimbits.com/&quot; title=&quot;Visit nimbits page&quot;&gt;Nimbits&lt;/a&gt; which is designed as an operational process historian and would likely be very good at storing discrete counter and numeric values (similar to graphite or an RRD tool). I also found &lt;a href=&quot;https://metrics.librato.com/&quot; title=&quot;Visit Metrics at Librato&quot; target=_&quot;blank&quot;&gt;Metrics&lt;/a&gt;, which appears to be a much better fit for instrumentation then these logging services and is worth considering for a future instrumentation post. Unfortunately these options are a different enough model that I couldn&#039;t compare them side-by-side with the logging services in this post.&lt;/p&gt;

&lt;p&gt;It is certainly possible to instrument your applications with limited impact and modification to the source application, however most of the logging services I could find don&#039;t offer the type of support you would want for that type of exercise. Splunk Storm seemed to offer the closest capabilities, but the service has the ability to be unavailable when you need it the most. That being said, many of the services would probably shine when used for the purpose they were created for and the fact that I couldn&#039;t misuse them for this other purpose shouldn&#039;t dissuade you for looking into them for log management and analytics purposes.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>There is a lot of value in knowing what the internals of your application are doing and, more importantly, knowing them 5 minutes ago before someone called to complain the system is slow. Instrumenting an application sounds like a complex task, but in <a href="http://blogs.lessthandot.com/index.php/All/?p=1748" title="Monitoring and Logging as a Service - The Common Bits">my prior post</a> I showed some sample code that allowed me to tie into several log services to store data from my system as it is running. </p>

<p>Keep in mind, I have extremely high hopes for these types of services, due to my experience in instrumentation and logging in manufacturing environments (<a href="http://blogs.lessthandot.com/index.php/All/?p=1747" title="Monitoring and Logging as a Service - Introduction">see the first post</a>). None of these systems are built with this task specifically in mind, so services that are absolutely wonderful at their core competencies may not prove to be a good fit for this specific use case.</p>

<p>In any case, let's see what we're going to be looking at:</p>
<li><a href="http://www.loggly.com/" title="Visit Loggly" target="_blank">Loggly</a> - "It's fast, fun and easy to use"</li>
<li><a href="http://www.datadoghq.com/" title="Visit DataDog" target="_blank">DataDog</a> - "On a mission to bring sanity to IT Management"</li>
<li><a href="https://www.splunkstorm.com/" title="Visit Splunk Storm" target="_blank">Splunk Storm</a> - "Your data has the answers, we help you find them."</li>
<li><a href="http://www.sumologic.com/" title="Visit Sumologic" target="_blank">Sumo  Logic</a> - "Make Your Applications Run Longer &amp; Stronger"</li>
<li><a href="http://www.logentries.com/" title="Visit logentries" target="_blank">logentries</a> - "We make your life easier"</li>
<li><a href="https://papertrailapp.com/" title="Visit papertrail" target="_blank">papertrail</a> - "Get back to work."</li>

<p>A few of these were non-starters, but I'll record what I know about them. The rest I'll present in similar formats for comparison.</p>

<h2>Loggly</h2>
<p>Loggly was one of the first logging services on the market as well as one of the first I instrumented with the application. </p>

<h3>Highlights:</h3>
<p>Loggly supports raw text, syslog, and JSON data posts and is a live service (not beta).</p>

<p><b>Input Support: </b> HTTP with raw text or JSON plus an array of syslog options<br />
<b>Ease of Setup: </b> No default inputs, have to create an initial HTTP JSON input which is fairly easy. Good samples provided for communicating with input.<br />
<b>Searching:</b> The search uses a console-like command line input for searches with limited direction and assistance (more later), I didn't invest the time that would have been necessary to learn enough of the search interface to become even a beginning user<br />
<b>Visualization: </b> Nonexistent. Has examples of how to build your own in python, jQuery, and google charts</p>

<h3>General Opinion:</h3>
<p>I would not use this service for application instrumentation. The search is the only feature it offers and that is in beta (Loggly has been live for several years). The command-line search idea is cute, but to me is an example of form over function that suffers from sluggish response times, low usability, and an unnecessary initial learning curve. My use of the site was characterized by extremely long loading times (30-60 seconds) and the lack of initial defaults for new users seems like a missed opportunity. If they were to focus on fixing the performance issues, improving the usability, and implementing visualization, I'd be ready to give them a second look.</p>

<h3>Running the Sample Site w/ Loggly</h3>
<p>Clone a copy of the sample application from <a href="https://github.com/tarwn/InstrumentationSampleCode" title="InstrumentationSampleCode on GitHub">tarwn/InstrumentationSampleCode on github</a>. Go to the <a href="http://www.loggly.com">Loggly.com website</a>, sign up for an account (you can get a free account by dragging the bars on the pricing page to the lowest values), create an HTTP input with JSON. </p>

<p>Once you have the input created, create or edit the <i>sensitive.config</i> file at the root of the solution with the following values (substitute your GUID in where appropriate):</p>

<p><b>sensitive.config</b></p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb62963'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb62963','cb56724'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb62963" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">LogProvider = Loggly</li><li style="" class="li2">Loggly.BaseURL = &lt;a href=&quot;https://logs.loggly.com/inputs/put-your-guid-here&quot;&gt;https://logs.loggly.com/inputs/put-your-guid-here&lt;/a&gt;</li></ol></div><div id="cb56724" style="display: none; color: red;"></div></div></div>

<p>Open the site, try a few things, then switch over to the Loggly screen and see if a page will load long enough to show it received some data.</p>

<h2>DataDog (Could Not Review)</h2>
<p><a href="http://www.datadoghq.com/" title="Visit DataDogHQ">DataDog</a> looked like it would be a good option, but after setting up my initial account I found I could not continue until I installed an agent on a server with a supported operating system. Unfortunately Windows is not supported (which was not obvious until you got signed up), so I was unable to continue. </p>

<p>They did email me a few days later to ask if I had any difficulties getting signed up since I hadn't finished the activation step, and the email looked personal and friendly, so I may return and see if they can help me get past that step to try the service for instrumentation purposes. </p>

<h2>Splunk Storm</h2>
<p><a href="https://www.splunkstorm.com/" title="Visit Splunk Storm" target="_blank">Splunk Storm</a> is a beta service from Splunk, which is a name that should be highly recognizable in the logging world. Storm is their beta service to offer the capabilities of Splunk without a local installation.</p>

<h3>Highlights: </h3>
<p><b>Input Support: </b> HTTP with raw text, syslog, netcat, and "any other application that transmits via TCP or UDP"<br />
<b>Ease of Setup: </b> Easy enough. API tokens are already available for use but error reporting on individual HTTP posts can sometimes be useless (501s for instance)<br />
<b>Searching:</b> The search for splunk is the bar to measure the others by. They use a text-based language but everything on the screen is clickable, common fields are captured and listed on the side for easy searching, and the search language itself is something you can pick up a bit at a time<br />
<b>Visualization: </b> Pretty decent but there are some definite learning curve moments and when you try to graph too much data it throws away parts of the dataset entirely</p>

<h3>General Opinion:</h3>
<p>I have been using Splunk Storm for longer than any of the other services. The service itself is very promising. It offers a really good search capability with a great drilldown capability and setup was easy. Often it will give mini-help boxes when typing terms into the search and suggest similar searches I have done recently, details on commands, and suggestions for commands I may want to use.</p>

<p>Unfortunately they are using the bad definition of "beta", instead of the "it's relatively stable but not feature complete" definition. There have been at least two occurrences where I was locked out of the system while they performed maintenance, on one occasion an update actually locked me out of the main account for 4-5 days (with a constant friendly reminder that service would be back momentarily). Documentation links are present throughout the application but most of them lead to 404 pages. The fact that charts throw away portions of the dataset when you ask them to chart too much can be incredibly frustrating (when you figure out what it's doing). </p>

<p>After the initial exposure I started to find that I was spending more and more time in the splunk documentation (once you figure out how to get there) simply trying to learn their command-line language because the interface only gets you so far. The power of the tool is impressive, but the level of effort I would have to spend learning how to use their tool is a common annoyance that reminds me of Loggly's command-line tool. I've also seen more of their 404 narwhal page then I ever expected to see, as apparently any page that is left open for too long refreshes and drops you there (session time out perhaps?).</p>

<h3>Running the Sample Site w/ Splunk Storm:</h3>
<p>Clone a copy of the sample application from <a href="https://github.com/tarwn/InstrumentationSampleCode" title="InstrumentationSampleCode on GitHub">tarwn/InstrumentationSampleCode on github</a>. Go to the <a href="http://www.splunkstorm.com" target="_blank">splunkstorm.com website</a> and sign up for an account (all accounts are free while it is in beta).</p>

<p>In the inputs section of the project there is a tab called "API" that has the access token and project id. Add or edit the <i>sensitive.config</i> file at the root of the .Net solution, substituting your values where appropriate:</p>

<p><b>sensitive.config</b></p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb46093'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb46093','cb12456'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb46093" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">LogProvider = storm</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">Storm.AccessToken = your-access-token-here</li><li style="" class="li2">Storm.BaseURL = &lt;a href=&quot;https://api.splunkstorm.com/1/inputs/http&quot;&gt;https://api.splunkstorm.com/1/inputs/http&lt;/a&gt;</li><li style="" class="li1">Storm.ProjectId = your-project-id-here</li></ol></div><div id="cb12456" style="display: none; color: red;"></div></div></div>

<p>Load up the sample app, click some buttons, then switch over to Splunk Storm and try out the search. Clicking different items in the log entries will fire off searches. Unfortunately charting is almost a blog series on it's own, sorry.</p>

<h2>Sumo Logic (Could Not Review)</h2>
<p><a href="http://www.sumologic.com" title="Visit Sumo Logic" target="_blank">Sumo Logic</a> claims to provide "the first cloud-based log management and analytics solution". The demo video shows a product that has similar capabilities to Splunk in the search area with additions of automatic pattern detection and monitoring. Initially I did not demo the product because they don't offer unattended demos, you have to sign up and wait for someone to contact you to hold your hand while you demo their service. Later, as I was reading their FAQ, I found that they do not offer an API, accepting data only via collectors that are installed on your local system. The collector idea isn't bad, manufacturing historians have been doing this for a couple decades after all, but without demoing I couldn't tell how easy or hard it would be to install them and automate that installation for those of us with large numbers of servers or elastic infrastructure.</p>

<h2>logentries</h2>
<p><a href="http://logentries.com//" title="Visit logentries" target="_blank">logentries</a> is a cloud logging service that has been on the market for over a year and offers free and metered accounts.</p>

<h3>Highlights: </h3>
<p><b>Input Support: </b> HTTP with raw text, agents, or an array of syslog options<br />
<b>Ease of Setup: </b> No default inputs, have to create an initial log. Doesn't respond to success or error HTTP calls, making troubleshooting difficult. Initial data didn't show in site until I specifically hit the refresh button in the top right of the app, leading to several hours of frustrated and unnecessary troubleshooting initially.<br />
<b>Searching:</b> The search seems fairly basic, but they pattern matching and tagging ability lends a second dimension to search capabilities. Creating patterns was not straightforward, however.<br />
<b>Visualization: </b> Graphs are purely around tags and data records, no ability to extract values from entries and graph them (so somewhere between loggly and splunk in this regard)</p>

<h3>General Opinion:</h3>
<p>Logentries doesn't seem like a good solution for application instrumentation, but this is more due to their focus being different from what I was looking for. I did have difficulties with the initial setup due to the lack of response from their HTTP service, the fact that all of their language examples use raw TCP sockets instead of HTTP libraries, and the delays in data being sent and actually showing up in the interface.</p>

<p> I also found the search and pattern sections frustrating. That being said, some of this was due to me trying to misuse their service for instrumentation and because their documentation is not linked from inside the interface. The search supports regular expressions and provides a good number of examples, which makes it almost as powerful as the other services without the need to learn a whole new syntax or language. </p>

<p>With one exception, if I was looking for a log monitoring and alerting system I would consider starting here because I could get farther for a reduced learning curve and startup time.  Unfortunately the exception is that I can't tell if my data is actually reaching them or not. Switching to the TCP method they outline may or may not resolve this, but the lack of error feedback even if I got it working with that method.</p>

<h3>Running the Sample Site w/ logentries:</h3>
<p>Clone a copy of the sample application from <a href="https://github.com/tarwn/InstrumentationSampleCode" title="InstrumentationSampleCode on GitHub">tarwn/InstrumentationSampleCode on github</a>. Go to the <a href="http://www.logentries.com" target="_blank">logentries.com website</a> and sign up for an account (there is a free account with limited storage).</p>

<p>Add a new Host entry, then inside that host add a new Log with a type of "API Library/HTML". Add or edit the <i>sensitive.config</i> file at the root of the .Net solution, substituting your values where appropriate:</p>

<p><b>sensitive.config</b></p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb50187'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb50187','cb75700'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb50187" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">LogProvider = logentries</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">logentries.BaseURL = &lt;a href=&quot;http://api.logentries.com&quot;&gt;http://api.logentries.com&lt;/a&gt;</li><li style="" class="li2">logentries.AccountKey = your-account-key-here</li><li style="" class="li1">logentries.Host = your-host-name</li><li style="" class="li2">logentries.Log = your-log-name-or-key</li></ol></div><div id="cb75700" style="display: none; color: red;"></div></div></div>

<p>Note: I fired up the sample app pointed at logentries when I started writing this up, my data still hasn't shown up. I'm not sure if it will or not.</p>

<h2>Papertrail (Could Not Review)</h2>
<p><a href="https://papertrailapp.com/" title="Visit papertrail" target="_blank">Papertrail</a> looks like a pretty solid service, from their front page. Unfortunately while they do support a large number of systems as log inputs, a REST API is not one of them. They do support syslog as an input source, however, so it should be possible to communicate from an instrumented app over TCP using the syslog format.After digging into more of their documentation, however, it looks like this system could offer some instrumentation support from an alerting perspective, but doesn't really offer the types of capabilities we would want for instrumentation (aggregating and analyzing values in like log entries, for instance API executing times across multiple calls). </p>

<h2>Summary</h2>
<p>Most instrumentation data options require local installers, many based on RRD storage or other, similar methods. I did find an open source server called <a href="http://www.nimbits.com/" title="Visit nimbits page">Nimbits</a> which is designed as an operational process historian and would likely be very good at storing discrete counter and numeric values (similar to graphite or an RRD tool). I also found <a href="https://metrics.librato.com/" title="Visit Metrics at Librato" target=_"blank">Metrics</a>, which appears to be a much better fit for instrumentation then these logging services and is worth considering for a future instrumentation post. Unfortunately these options are a different enough model that I couldn't compare them side-by-side with the logging services in this post.</p>

<p>It is certainly possible to instrument your applications with limited impact and modification to the source application, however most of the logging services I could find don't offer the type of support you would want for that type of exercise. Splunk Storm seemed to offer the closest capabilities, but the service has the ability to be unavailable when you need it the most. That being said, many of the services would probably shine when used for the purpose they were created for and the fact that I couldn't misuse them for this other purpose shouldn't dissuade you for looking into them for log management and analytics purposes.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-reviews#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1749</wfw:commentRss>
		</item>
				<item>
			<title>Monitoring and Logging as a Service - The Common Bits</title>
			<link>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits</link>
			<pubDate>Tue, 03 Jul 2012 10:01:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Instrumentation</category>			<guid isPermaLink="false">1748@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;In my &lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service&quot; title=&quot;Monitoring and Logging as a Service - Introduction&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt; I outlined some of my own history with monitoring and my intent to review several available logging services. To help compare apples to apples, the logging mechanisms and log messages will operate consistently for each of the selected services. &lt;/p&gt;

&lt;p&gt;The sample code is available on github and will continue to be updated as I add services I am trying: &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode&quot; title=&quot;InstrumentationSampleCode on GitHub&quot; target=&quot;_blank&quot;&gt;tarwn/InstrumentationSampleCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purpose of this post is to outline that common portion of the application before we get into the reviews, so we have a common starting place and something to refer back to once we start customizing to support the services.&lt;/p&gt;

&lt;h2&gt;The Sample Code&lt;/h2&gt;
&lt;p&gt;The sample code for this series is relatively simple. The solution includes 4 projects: Logging, LoggingTests, SampleSiteWithLogging, and SensitiveSettings.&lt;/p&gt;

&lt;h3&gt;Logging&lt;/h3&gt;
&lt;p&gt;This is the core project for the post series. The goal of the Logging library is to make it extremely easy to log from inside the application (think statsd + graphite ala &lt;a href=&quot;http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/&quot; title=&quot;Measure Anything, Measure Everything post at Etsy&quot; target=&quot;_blank&quot;&gt;Etsy&lt;/a&gt; simple) while making it easy to swap out which logging service we are using. &lt;/p&gt;

&lt;p&gt;There are two major components in the library, the Logger class which provides singleton access to the selected logging service and the required one line log calls and the HttpJsonPost, which handles HTTP requests (currently focused on key/value data for unstructured JSON or raw text posts).&lt;/p&gt;

&lt;p&gt;The singleton can be setup with any implementation of an ILogProvider. It provides methods to log a single message or produce a message that automatically logs itself on disposal (capturing elapsed time and including it in the message). &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Logging.Logger&lt;/b&gt; (&lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Logger.cs&quot; title=&quot;Logger.cs on Github&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb21735&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; Logger &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; ILogProvider _logProvider;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; Logger _current;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; Logger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ILogProvider logProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _logProvider = logProvider;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; LogMessage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Dictionary&amp;lt;string, string&amp;gt; message, Action&amp;lt;Result&amp;gt; callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _logProvider.&lt;span style=&quot;color: #0000FF;&quot;&gt;Log&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;message, callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; SetDefaultLogger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;ILogProvider logProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; _current = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Logger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;logProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; Logger GetDefaultLogger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;_current != &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; _current;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;throw&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Exception&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Default logger not setup&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Log&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Dictionary&amp;lt;string, string&amp;gt; message, Action&amp;lt;Result&amp;gt; callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; GetDefaultLogger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;LogMessage&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;message, callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; LoggerWithElapsedTime CaptureElapsedTime&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Dictionary&amp;lt;System.&lt;span style=&quot;color: #FF0000;&quot;&gt;String&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;String&lt;/span&gt;&amp;gt; message, Action&amp;lt;Result&amp;gt; callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; LoggerWithElapsedTime&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;GetDefaultLogger&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;, message, callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb34681&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each log call passes in a message and a callback that will be called with the result of the HTTP post, though so far the sample website code is using fire-and-forget, not bothering to provide a callback method.&lt;/p&gt;

&lt;p&gt;To support the ILogProvider implementation, I created an &lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Communications/HttpJsonPost.cs&quot; title=&quot;HttpJsonPost on Github&quot;&gt;HttpJsonPost class&lt;/a&gt; class and associated helpers to handle the HTTP communications. This class can do synchronous and asynchronous HTTP requests to the relevant services:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Logging.Communications.HttpJsonPost&lt;/b&gt; (&lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Communications/HttpJsonPost.cs&quot; title=&quot;HttpJsonPost on Github&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb79129&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; HttpJsonPost &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Dictionary&amp;lt;string,string&amp;gt; _message;&lt;br /&gt;&amp;nbsp; &amp;nbsp; NetworkCredential _credentials;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; _useJson;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; HttpJsonPost&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Dictionary&amp;lt;string, string&amp;gt; message, NetworkCredential credentials = &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;, &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; useJson = &lt;span style=&quot;color: #0600FF;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; HttpWebRequest InitializeRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; url, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; method&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Send&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; url, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; method, Action&amp;lt;Result&amp;gt; callback, &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; processResponse = &lt;span style=&quot;color: #0600FF;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; SendAsync&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; url, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; method, Action&amp;lt;Result&amp;gt; callback, &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; processResponse = &lt;span style=&quot;color: #0600FF;&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; GetRequestStream&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IAsyncResult result&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; GetResponseStream&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;IAsyncResult result&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ProcessResponse&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Func&amp;lt;WebResponse&amp;gt; getResponse, Action&amp;lt;Result&amp;gt; callback&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; WriteMessage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Stream stream&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb3681&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This little bit of code and their supporting classes are all it takes to talk to the external logging services, and to do so with limited impact by using asynchronous requests and callbacks.&lt;/p&gt;

&lt;h2&gt;The Sample Website&lt;/h2&gt;
&lt;p&gt;The sample website contains very little code that I added. The majority of the code is the &quot;Empty MVC 3 Web Site&quot; template from visual studio, I&#039;ve added an extremely basic HomeController and some setup code in the global.asax to setup the log provider and log a few events.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;SampleSiteWithLogging.Global&lt;/b&gt; (&lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode/blob/master/SampleSiteWithLogging/Global.asax.cs&quot; title=&quot;Global.asax.cs on GitHub&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb64280&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; MvcApplication : &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Web&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;HttpApplication&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Application_Start&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;/* ... */&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ILogProvider provider = GetProviderFromSettings&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Logger.&lt;span style=&quot;color: #0000FF;&quot;&gt;SetDefaultLogger&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;provider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Logger.&lt;span style=&quot;color: #0000FF;&quot;&gt;Log&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Dictionary&amp;lt;string, string&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;ApplicationStartup&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Time&amp;quot;&lt;/span&gt;, DateTime.&lt;span style=&quot;color: #0000FF;&quot;&gt;UtcNow&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Application_BeginRequest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Logger.&lt;span style=&quot;color: #0000FF;&quot;&gt;Log&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Dictionary&amp;lt;string, string&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;ApplicationRequest&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;UserAgent&amp;quot;&lt;/span&gt;, Request.&lt;span style=&quot;color: #0000FF;&quot;&gt;UserAgent&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Time&amp;quot;&lt;/span&gt;, DateTime.&lt;span style=&quot;color: #0000FF;&quot;&gt;UtcNow&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; Application_Error&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;Object&lt;/span&gt; sender, &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;EventArgs&lt;/span&gt; e&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Web&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;HttpContext&lt;/span&gt; context = HttpContext.&lt;span style=&quot;color: #0000FF;&quot;&gt;Current&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Exception&lt;/span&gt; exc = Context.&lt;span style=&quot;color: #0000FF;&quot;&gt;Server&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetLastError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Logger.&lt;span style=&quot;color: #0000FF;&quot;&gt;Log&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Dictionary&amp;lt;string, string&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;ApplicationError&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Exception&amp;quot;&lt;/span&gt;, exc.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Time&amp;quot;&lt;/span&gt;, DateTime.&lt;span style=&quot;color: #0000FF;&quot;&gt;UtcNow&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToString&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;protected&lt;/span&gt; ILogProvider GetProviderFromSettings&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;LoadFrom&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IO&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Path&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Combine&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;AppDomain&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;CurrentDomain&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;BaseDirectory&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;@&quot;bin\&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; provider = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;null&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;ContainsKey&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;LogProvider&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; provider = SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;LogProvider&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;switch&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;provider.&lt;span style=&quot;color: #0000FF;&quot;&gt;ToUpper&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;case&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;LOGGLY&amp;quot;&lt;/span&gt;:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; LogglyProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Loggly.BaseURL&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;case&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;STORM&amp;quot;&lt;/span&gt;:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; StormProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Storm.BaseURL&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;, SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Storm.AccessToken&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;, SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Storm.ProjectId&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;case&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;LOGENTRIES&amp;quot;&lt;/span&gt;:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; LogentriesProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;logentries.BaseURL&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;, SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;logentries.AccountKey&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;, SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;logentries.Host&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;, SensitiveSettings.&lt;span style=&quot;color: #0000FF;&quot;&gt;SettingsManager&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Settings&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;logentries.Log&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;default&lt;/span&gt;:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; NullLogProvider&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb92485&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The global.asax file allows us to wire logic into the global application workflow. On startup we get a provider, based on our &quot;Sensitive Settings&quot; configuration file, set that as our default logger, then go ahead and log our first message with entries to indicate this is application startup and the current UTC timestamp. Each time we receive a request from a web browser, we use our on line logging call to log the browsers UserAgent string and a timestamp. When an error goes unhandled, we can log that too.&lt;/p&gt;

&lt;p&gt;Elsewhere in our application we can use those same one line calls to pass information to the logging service. The HomeController logs information, but instead uses the CaptureElapsedTime method to log information and the time that elapses between it&#039;s instantiation and disposal.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;SampleSiteWithLogging.Controllers.HomeController&lt;/b&gt; (&lt;a href=&quot;https://github.com/tarwn/InstrumentationSampleCode/blob/master/SampleSiteWithLogging/Controllers/HomeController.cs&quot; title=&quot;HomeController.cs on Github&quot; target=&quot;_blank&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb31760&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; HomeController : Controller &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; ActionResult Index&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; ActionResult ShortOperation&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;var log = Logging.&lt;span style=&quot;color: #0000FF;&quot;&gt;Logger&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;CaptureElapsedTime&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Dictionary&amp;lt;string, string&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;SiteHit&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Area&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;HomeController&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Method&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;ShortOperation&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Index&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; ActionResult LongOperation&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;var log = Logging.&lt;span style=&quot;color: #0000FF;&quot;&gt;Logger&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;CaptureElapsedTime&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Dictionary&amp;lt;string, string&amp;gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Type&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;SiteHit&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Area&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;HomeController&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Method&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;LongOperation&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Thread.&lt;span style=&quot;color: #0000FF;&quot;&gt;Sleep&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;3000&lt;/span&gt; * &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Random&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;NextDouble&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; View&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Index&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb22957&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These examples are actually more wordy than I would like. If I were building this as part of a production application, I would refactor them down to take explicit arguments or infer some of the values, reducing the size of each call even further. &lt;/p&gt;

&lt;h2&gt;&quot;Sensitive Settings&quot;&lt;/h2&gt;
&lt;p&gt;The Sensitive Settings library is just a quick settings library I threw together so I could prevent my API keys for these services from getting committed to github.  The library is referenced by the website and unit test projects, each of which have a prebuild step to copy the &quot;sensitive.config&quot; file from the solution folder to their build target folders.&lt;/p&gt;

&lt;h2&gt;LoggingTests&lt;/h2&gt;
&lt;p&gt;The LoggingTests folder is really just a way for me to easily test small chunks of the libraries without firing up the interface. These aren&#039;t real unit tests and that should serve as a second reason not to download and attempt to use the Logging library in a production application. &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;

&lt;h2&gt;Onward to the Reviews&lt;/h2&gt;
&lt;p&gt;Now that we have some code we can easily connect to all of the services, the next post will cover the services I tried and how they measured up against my expectations for this type of usage.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>In my <a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service" title="Monitoring and Logging as a Service - Introduction" target="_blank">previous post</a> I outlined some of my own history with monitoring and my intent to review several available logging services. To help compare apples to apples, the logging mechanisms and log messages will operate consistently for each of the selected services. </p>

<p>The sample code is available on github and will continue to be updated as I add services I am trying: <a href="https://github.com/tarwn/InstrumentationSampleCode" title="InstrumentationSampleCode on GitHub" target="_blank">tarwn/InstrumentationSampleCode</a></p>

<p>The purpose of this post is to outline that common portion of the application before we get into the reviews, so we have a common starting place and something to refer back to once we start customizing to support the services.</p>

<h2>The Sample Code</h2>
<p>The sample code for this series is relatively simple. The solution includes 4 projects: Logging, LoggingTests, SampleSiteWithLogging, and SensitiveSettings.</p>

<h3>Logging</h3>
<p>This is the core project for the post series. The goal of the Logging library is to make it extremely easy to log from inside the application (think statsd + graphite ala <a href="http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/" title="Measure Anything, Measure Everything post at Etsy" target="_blank">Etsy</a> simple) while making it easy to swap out which logging service we are using. </p>

<p>There are two major components in the library, the Logger class which provides singleton access to the selected logging service and the required one line log calls and the HttpJsonPost, which handles HTTP requests (currently focused on key/value data for unstructured JSON or raw text posts).</p>

<p>The singleton can be setup with any implementation of an ILogProvider. It provides methods to log a single message or produce a message that automatically logs itself on disposal (capturing elapsed time and including it in the message). </p>

<p><b>Logging.Logger</b> (<a href="https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Logger.cs" title="Logger.cs on Github" target="_blank">source</a>)</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb55434'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb55434','cb20867'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb55434" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> Logger <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> ILogProvider _logProvider;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">static</span> Logger _current;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> Logger<span style="color: #000000;">&#40;</span>ILogProvider logProvider<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; _logProvider = logProvider;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> LogMessage<span style="color: #000000;">&#40;</span>Dictionary&lt;string, string&gt; message, Action&lt;Result&gt; callback<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; _logProvider.<span style="color: #0000FF;">Log</span><span style="color: #000000;">&#40;</span>message, callback<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> SetDefaultLogger<span style="color: #000000;">&#40;</span>ILogProvider logProvider<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; _current = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Logger<span style="color: #000000;">&#40;</span>logProvider<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">static</span> Logger GetDefaultLogger<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>_current != <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> _current;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">else</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">throw</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Exception<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Default logger not setup&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">static</span> <span style="color: #0600FF;">void</span> Log<span style="color: #000000;">&#40;</span>Dictionary&lt;string, string&gt; message, Action&lt;Result&gt; callback<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; GetDefaultLogger<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">LogMessage</span><span style="color: #000000;">&#40;</span>message, callback<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">static</span> LoggerWithElapsedTime CaptureElapsedTime<span style="color: #000000;">&#40;</span>Dictionary&lt;System.<span style="color: #FF0000;">String</span>, <span style="color: #000000;">System</span>.<span style="color: #0000FF;">String</span>&gt; message, Action&lt;Result&gt; callback<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> LoggerWithElapsedTime<span style="color: #000000;">&#40;</span>GetDefaultLogger<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>, message, callback<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb20867" style="display: none; color: red;"></div></div></div>

<p>Each log call passes in a message and a callback that will be called with the result of the HTTP post, though so far the sample website code is using fire-and-forget, not bothering to provide a callback method.</p>

<p>To support the ILogProvider implementation, I created an <a href="https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Communications/HttpJsonPost.cs" title="HttpJsonPost on Github">HttpJsonPost class</a> class and associated helpers to handle the HTTP communications. This class can do synchronous and asynchronous HTTP requests to the relevant services:</p>

<p><b>Logging.Communications.HttpJsonPost</b> (<a href="https://github.com/tarwn/InstrumentationSampleCode/blob/master/Logging/Communications/HttpJsonPost.cs" title="HttpJsonPost on Github" target="_blank">source</a>)</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb64983'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb64983','cb3111'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb64983" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> HttpJsonPost <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; Dictionary&lt;string,string&gt; _message;</li><li style="" class="li2">&nbsp; &nbsp; NetworkCredential _credentials;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #FF0000;">bool</span> _useJson;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> HttpJsonPost<span style="color: #000000;">&#40;</span>Dictionary&lt;string, string&gt; message, NetworkCredential credentials = <span style="color: #0600FF;">null</span>, <span style="color: #FF0000;">bool</span> useJson = <span style="color: #0600FF;">true</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> HttpWebRequest InitializeRequest<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> url, <span style="color: #FF0000;">string</span> method<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> Send<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> url, <span style="color: #FF0000;">string</span> method, Action&lt;Result&gt; callback, <span style="color: #FF0000;">bool</span> processResponse = <span style="color: #0600FF;">true</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> SendAsync<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> url, <span style="color: #FF0000;">string</span> method, Action&lt;Result&gt; callback, <span style="color: #FF0000;">bool</span> processResponse = <span style="color: #0600FF;">true</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">void</span> GetRequestStream<span style="color: #000000;">&#40;</span>IAsyncResult result<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">void</span> GetResponseStream<span style="color: #000000;">&#40;</span>IAsyncResult result<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">void</span> ProcessResponse<span style="color: #000000;">&#40;</span>Func&lt;WebResponse&gt; getResponse, Action&lt;Result&gt; callback<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">private</span> <span style="color: #0600FF;">void</span> WriteMessage<span style="color: #000000;">&#40;</span>Stream stream<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #008080; font-style: italic;">/* ... */</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb3111" style="display: none; color: red;"></div></div></div>

<p>This little bit of code and their supporting classes are all it takes to talk to the external logging services, and to do so with limited impact by using asynchronous requests and callbacks.</p>

<h2>The Sample Website</h2>
<p>The sample website contains very little code that I added. The majority of the code is the "Empty MVC 3 Web Site" template from visual studio, I've added an extremely basic HomeController and some setup code in the global.asax to setup the log provider and log a few events.</p>

<p><b>SampleSiteWithLogging.Global</b> (<a href="https://github.com/tarwn/InstrumentationSampleCode/blob/master/SampleSiteWithLogging/Global.asax.cs" title="Global.asax.cs on GitHub" target="_blank">source</a>)</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb38325'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb38325','cb35032'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb38325" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> MvcApplication : <span style="color: #000000;">System</span>.<span style="color: #0000FF;">Web</span>.<span style="color: #0000FF;">HttpApplication</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; </li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #008080; font-style: italic;">/* ... */</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">void</span> Application_Start<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008080; font-style: italic;">/* ... */</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; ILogProvider provider = GetProviderFromSettings<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Logger.<span style="color: #0000FF;">SetDefaultLogger</span><span style="color: #000000;">&#40;</span>provider<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Logger.<span style="color: #0000FF;">Log</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Dictionary&lt;string, string&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Type&quot;</span>, <span style="color: #808080;">&quot;ApplicationStartup&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Time&quot;</span>, DateTime.<span style="color: #0000FF;">UtcNow</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#125;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">void</span> Application_BeginRequest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Logger.<span style="color: #0000FF;">Log</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Dictionary&lt;string, string&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Type&quot;</span>, <span style="color: #808080;">&quot;ApplicationRequest&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;UserAgent&quot;</span>, Request.<span style="color: #0000FF;">UserAgent</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Time&quot;</span>, DateTime.<span style="color: #0000FF;">UtcNow</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#125;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">protected</span> <span style="color: #0600FF;">void</span> Application_Error<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">Object</span> sender, <span style="color: #000000;">System</span>.<span style="color: #0000FF;">EventArgs</span> e<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">System</span>.<span style="color: #0000FF;">Web</span>.<span style="color: #0000FF;">HttpContext</span> context = HttpContext.<span style="color: #0000FF;">Current</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">System</span>.<span style="color: #0000FF;">Exception</span> exc = Context.<span style="color: #0000FF;">Server</span>.<span style="color: #0000FF;">GetLastError</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Logger.<span style="color: #0000FF;">Log</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Dictionary&lt;string, string&gt;<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Type&quot;</span>, <span style="color: #808080;">&quot;ApplicationError&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Exception&quot;</span>, exc.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Time&quot;</span>, DateTime.<span style="color: #0000FF;">UtcNow</span>.<span style="color: #0000FF;">ToString</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#125;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">protected</span> ILogProvider GetProviderFromSettings<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">LoadFrom</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">System</span>.<span style="color: #0000FF;">IO</span>.<span style="color: #0000FF;">Path</span>.<span style="color: #0000FF;">Combine</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">System</span>.<span style="color: #0000FF;">AppDomain</span>.<span style="color: #0000FF;">CurrentDomain</span>.<span style="color: #0000FF;">BaseDirectory</span>, <span style="color: #808080;">@"bin\&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #FF0000;">string</span> provider = <span style="color: #808080;">&quot;null&quot;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span>.<span style="color: #0000FF;">ContainsKey</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;LogProvider&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; provider = SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;LogProvider&quot;</span><span style="color: #000000;">&#93;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">switch</span> <span style="color: #000000;">&#40;</span>provider.<span style="color: #0000FF;">ToUpper</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> </li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">case</span> <span style="color: #808080;">&quot;LOGGLY&quot;</span>:</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> LogglyProvider<span style="color: #000000;">&#40;</span>SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Loggly.BaseURL&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">case</span> <span style="color: #808080;">&quot;STORM&quot;</span>:</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> StormProvider<span style="color: #000000;">&#40;</span>SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Storm.BaseURL&quot;</span><span style="color: #000000;">&#93;</span>, SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Storm.AccessToken&quot;</span><span style="color: #000000;">&#93;</span>, SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;Storm.ProjectId&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">case</span> <span style="color: #808080;">&quot;LOGENTRIES&quot;</span>:</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> LogentriesProvider<span style="color: #000000;">&#40;</span>SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;logentries.BaseURL&quot;</span><span style="color: #000000;">&#93;</span>, SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;logentries.AccountKey&quot;</span><span style="color: #000000;">&#93;</span>, SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;logentries.Host&quot;</span><span style="color: #000000;">&#93;</span>, SensitiveSettings.<span style="color: #0000FF;">SettingsManager</span>.<span style="color: #0000FF;">Settings</span><span style="color: #000000;">&#91;</span><span style="color: #808080;">&quot;logentries.Log&quot;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">default</span>:</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> NullLogProvider<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; </li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb35032" style="display: none; color: red;"></div></div></div>

<p>The global.asax file allows us to wire logic into the global application workflow. On startup we get a provider, based on our "Sensitive Settings" configuration file, set that as our default logger, then go ahead and log our first message with entries to indicate this is application startup and the current UTC timestamp. Each time we receive a request from a web browser, we use our on line logging call to log the browsers UserAgent string and a timestamp. When an error goes unhandled, we can log that too.</p>

<p>Elsewhere in our application we can use those same one line calls to pass information to the logging service. The HomeController logs information, but instead uses the CaptureElapsedTime method to log information and the time that elapses between it's instantiation and disposal.</p>

<p><b>SampleSiteWithLogging.Controllers.HomeController</b> (<a href="https://github.com/tarwn/InstrumentationSampleCode/blob/master/SampleSiteWithLogging/Controllers/HomeController.cs" title="HomeController.cs on Github" target="_blank">source</a>)</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb64538'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb64538','cb99897'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb64538" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> HomeController : Controller <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> ActionResult Index<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> View<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> ActionResult ShortOperation<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>var log = Logging.<span style="color: #0000FF;">Logger</span>.<span style="color: #0000FF;">CaptureElapsedTime</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Dictionary&lt;string, string&gt; <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Type&quot;</span>, <span style="color: #808080;">&quot;SiteHit&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Area&quot;</span>, <span style="color: #808080;">&quot;HomeController&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Method&quot;</span>, <span style="color: #808080;">&quot;ShortOperation&quot;</span> <span style="color: #000000;">&#125;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> View<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Index&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> ActionResult LongOperation<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>var log = Logging.<span style="color: #0000FF;">Logger</span>.<span style="color: #0000FF;">CaptureElapsedTime</span><span style="color: #000000;">&#40;</span><a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Dictionary&lt;string, string&gt; <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Type&quot;</span>, <span style="color: #808080;">&quot;SiteHit&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Area&quot;</span>, <span style="color: #808080;">&quot;HomeController&quot;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #000000;">&#123;</span> <span style="color: #808080;">&quot;Method&quot;</span>, <span style="color: #808080;">&quot;LongOperation&quot;</span> <span style="color: #000000;">&#125;</span> <span style="color: #000000;">&#125;</span>, <span style="color: #0600FF;">null</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Thread.<span style="color: #0000FF;">Sleep</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">int</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#40;</span><span style="color: #FF0000;">3000</span> * <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Random<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">NextDouble</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> View<span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Index&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb99897" style="display: none; color: red;"></div></div></div>

<p>These examples are actually more wordy than I would like. If I were building this as part of a production application, I would refactor them down to take explicit arguments or infer some of the values, reducing the size of each call even further. </p>

<h2>"Sensitive Settings"</h2>
<p>The Sensitive Settings library is just a quick settings library I threw together so I could prevent my API keys for these services from getting committed to github.  The library is referenced by the website and unit test projects, each of which have a prebuild step to copy the "sensitive.config" file from the solution folder to their build target folders.</p>

<h2>LoggingTests</h2>
<p>The LoggingTests folder is really just a way for me to easily test small chunks of the libraries without firing up the interface. These aren't real unit tests and that should serve as a second reason not to download and attempt to use the Logging library in a production application. <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p>

<h2>Onward to the Reviews</h2>
<p>Now that we have some code we can easily connect to all of the services, the next post will cover the services I tried and how they measured up against my expectations for this type of usage.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/EnterpriseDev/instrumentation/monitoring-and-logging-as-a-service-common-bits#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/EnterpriseDev/?tempskin=_rss2&#38;disp=comments&#38;p=1748</wfw:commentRss>
		</item>
			</channel>
</rss>
