├── .gitattributes ├── .gitignore ├── BuildAsCode ├── Build-as-Code.pdf ├── Demo │ ├── .dockerignore │ ├── Nuke │ │ ├── .nuke │ │ │ ├── build.schema.json │ │ │ └── parameters.json │ │ ├── Buldac.sln │ │ ├── build.cmd │ │ ├── build.ps1 │ │ ├── build.sh │ │ ├── build │ │ │ ├── .editorconfig │ │ │ ├── Build.cs │ │ │ ├── Build.csproj │ │ │ ├── Build.csproj.DotSettings │ │ │ ├── Configuration.cs │ │ │ ├── Directory.Build.props │ │ │ ├── Directory.Build.targets │ │ │ └── Dockerfile │ │ ├── share │ │ │ └── Build.Common │ │ │ │ ├── Build.Common.csproj │ │ │ │ ├── IBaseBuild.cs │ │ │ │ ├── IDefaultBuild.cs │ │ │ │ ├── IDockerBuild.cs │ │ │ │ ├── INuGetBuild.cs │ │ │ │ ├── IReleaseBuild.cs │ │ │ │ └── Integration │ │ │ │ ├── ApplicationVersion.cs │ │ │ │ ├── BaseBuildExtensions.cs │ │ │ │ ├── DockerBuildExtensions.cs │ │ │ │ ├── PullRequest.cs │ │ │ │ └── StringExtensions.cs │ │ ├── src │ │ │ └── Buldac │ │ │ │ ├── Buldac.csproj │ │ │ │ ├── Controllers │ │ │ │ └── WeatherForecastController.cs │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ │ ├── WeatherForecast.cs │ │ │ │ ├── appsettings.Development.json │ │ │ │ └── appsettings.json │ │ └── tests │ │ │ └── Buldac.Tests │ │ │ ├── Buldac.Tests.csproj │ │ │ └── UnitTest1.cs │ ├── PowerShell │ │ ├── Buldac.sln │ │ ├── build.ps1 │ │ ├── build │ │ │ ├── Builder.ps1 │ │ │ ├── Dockerfile │ │ │ ├── TeamCity.ps1 │ │ │ └── build-local.ps1 │ │ ├── src │ │ │ └── Buldac │ │ │ │ ├── Buldac.csproj │ │ │ │ ├── Controllers │ │ │ │ └── WeatherForecastController.cs │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ │ ├── WeatherForecast.cs │ │ │ │ ├── appsettings.Development.json │ │ │ │ └── appsettings.json │ │ └── tests │ │ │ └── Buldac.Tests │ │ │ ├── Buldac.Tests.csproj │ │ │ └── UnitTest1.cs │ └── README.md ├── Images │ └── Bender.png └── README.md ├── Metrix ├── Demo │ ├── ArchBenchmark │ │ ├── ArchBenchmark.cs │ │ ├── ArchBenchmark.csproj │ │ ├── InfluxExporter.cs │ │ ├── MyConfig.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── README.md │ │ ├── app.config │ │ └── packages.config │ ├── ArchDataGen │ │ ├── App.config │ │ ├── ArchDataGen.csproj │ │ ├── FakeDataGenerator.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── README.md │ │ └── packages.config │ ├── MetrixDemo.sln │ └── README.md ├── Images │ ├── ArchBenchmark.png │ ├── ArchDataGen.png │ └── Carl.jpg ├── Metrix.pdf └── README.md ├── README.md └── StructuredLogging ├── Demo ├── PowerGraphNet │ ├── App.config │ ├── ClusterConfig.cs │ ├── Controllers │ │ ├── AdController.cs │ │ ├── AuthController.cs │ │ ├── ImageController.cs │ │ ├── IndexController.cs │ │ ├── StaticController.cs │ │ └── TextController.cs │ ├── LoggerMiddleware.cs │ ├── PowerGraphNet.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Proto │ │ ├── Request.json │ │ └── Response.json │ ├── README.md │ ├── Scripts │ │ ├── Receive-Logs.ps1 │ │ ├── RegisterPorts.cmd │ │ └── Show-Requests.ps1 │ ├── Utils │ │ ├── Client.cs │ │ ├── HttpRequestHeadersExtensions.cs │ │ └── LogFactory.cs │ ├── WebServer.cs │ └── packages.config ├── README.md ├── SeqEditor │ ├── App.config │ ├── Document.cs │ ├── DocumentRepository.cs │ ├── OptimisticConcurrencyException.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── README.md │ ├── SeqEditor.csproj │ ├── Session.cs │ ├── Story.cs │ └── packages.config ├── SqlFraud │ ├── App.config │ ├── Order.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── README.md │ ├── Scripts │ │ ├── Fraud.sql │ │ └── Prepare.sql │ ├── SqlFraud.csproj │ └── packages.config └── StructuredLoggingDemo.sln ├── Images ├── Morpheus.jpg ├── PowerGraphNet.png ├── SeqEditor.png └── SqlFraud.png ├── README.md ├── StructuredLogging.pdf └── StructuredLogging.pptx /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | .vs 4 | .idea 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Rr]elease/ 15 | x64/ 16 | bld/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # Roslyn cache directories 21 | *.ide/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | #NUNIT 28 | *.VisualState.xml 29 | TestResult.xml 30 | 31 | # Build Results of an ATL Project 32 | [Dd]ebugPS/ 33 | [Rr]eleasePS/ 34 | dlldata.c 35 | 36 | *_i.c 37 | *_p.c 38 | *_i.h 39 | *.ilk 40 | *.meta 41 | *.obj 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.tlb 49 | *.tli 50 | *.tlh 51 | *.tmp 52 | *.tmp_proj 53 | *.log 54 | *.vspscc 55 | *.vssscc 56 | .builds 57 | *.pidb 58 | *.svclog 59 | *.scc 60 | 61 | # Chutzpah Test files 62 | _Chutzpah* 63 | 64 | # Visual C++ cache files 65 | ipch/ 66 | *.aps 67 | *.ncb 68 | *.opensdf 69 | *.sdf 70 | *.cachefile 71 | 72 | # Visual Studio profiler 73 | *.psess 74 | *.vsp 75 | *.vspx 76 | 77 | # TFS 2012 Local Workspace 78 | $tf/ 79 | 80 | # Guidance Automation Toolkit 81 | *.gpState 82 | 83 | # ReSharper is a .NET coding add-in 84 | _ReSharper*/ 85 | *.[Rr]e[Ss]harper 86 | *.DotSettings.user 87 | 88 | # JustCode is a .NET coding addin-in 89 | .JustCode 90 | 91 | # TeamCity is a build add-in 92 | _TeamCity* 93 | 94 | # DotCover is a Code Coverage Tool 95 | *.dotCover 96 | 97 | # NCrunch 98 | _NCrunch_* 99 | .*crunch*.local.xml 100 | 101 | # MightyMoose 102 | *.mm.* 103 | AutoTest.Net/ 104 | 105 | # Web workbench (sass) 106 | .sass-cache/ 107 | 108 | # Installshield output folder 109 | [Ee]xpress/ 110 | 111 | # DocProject is a documentation generator add-in 112 | DocProject/buildhelp/ 113 | DocProject/Help/*.HxT 114 | DocProject/Help/*.HxC 115 | DocProject/Help/*.hhc 116 | DocProject/Help/*.hhk 117 | DocProject/Help/*.hhp 118 | DocProject/Help/Html2 119 | DocProject/Help/html 120 | 121 | # Click-Once directory 122 | publish/ 123 | 124 | # Publish Web Output 125 | *.[Pp]ublish.xml 126 | *.azurePubxml 127 | ## TODO: Comment the next line if you want to checkin your 128 | ## web deploy settings but do note that will include unencrypted 129 | ## passwords 130 | #*.pubxml 131 | 132 | # NuGet Packages Directory 133 | packages/* 134 | ## TODO: If the tool you use requires repositories.config 135 | ## uncomment the next line 136 | #!packages/repositories.config 137 | 138 | # Enable "build/" folder in the NuGet Packages folder since 139 | # NuGet packages use it for MSBuild targets. 140 | # This line needs to be after the ignore of the build folder 141 | # (and the packages folder if the line above has been uncommented) 142 | !packages/build/ 143 | 144 | # Windows Azure Build Output 145 | csx/ 146 | *.build.csdef 147 | 148 | # Windows Store app package directory 149 | AppPackages/ 150 | 151 | # Others 152 | sql/ 153 | *.Cache 154 | ClientBin/ 155 | [Ss]tyle[Cc]op.* 156 | ~$* 157 | *~ 158 | *.dbmdl 159 | *.dbproj.schemaview 160 | *.pfx 161 | *.publishsettings 162 | node_modules/ 163 | 164 | # RIA/Silverlight projects 165 | Generated_Code/ 166 | 167 | # Backup & report files from converting an old project file 168 | # to a newer Visual Studio version. Backup files are not needed, 169 | # because we have git ;-) 170 | _UpgradeReport_Files/ 171 | Backup*/ 172 | UpgradeLog*.XML 173 | UpgradeLog*.htm 174 | 175 | # SQL Server files 176 | *.mdf 177 | *.ldf 178 | 179 | # Business Intelligence projects 180 | *.rdl.data 181 | *.bim.layout 182 | *.bim_*.settings 183 | 184 | # Microsoft Fakes 185 | FakesAssemblies/ 186 | 187 | # LightSwitch generated files 188 | GeneratedArtifacts/ 189 | _Pvt_Extensions/ 190 | ModelManifest.xml -------------------------------------------------------------------------------- /BuildAsCode/Build-as-Code.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/BuildAsCode/Build-as-Code.pdf -------------------------------------------------------------------------------- /BuildAsCode/Demo/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .vs/ 4 | .vscode/ 5 | .idea/ 6 | .dockerignore 7 | 8 | docs/ 9 | 10 | **/*.user 11 | **/*.md 12 | **/bin/ 13 | **/obj/ 14 | **/out/ 15 | 16 | Dockerfile 17 | README.md 18 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Build Schema", 4 | "$ref": "#/definitions/build", 5 | "definitions": { 6 | "build": { 7 | "type": "object", 8 | "properties": { 9 | "Branch": { 10 | "type": "string", 11 | "description": "Branch name" 12 | }, 13 | "BuildCounter": { 14 | "type": "string", 15 | "description": "Build counter" 16 | }, 17 | "Continue": { 18 | "type": "boolean", 19 | "description": "Indicates to continue a previously failed build attempt" 20 | }, 21 | "DockerProjectName": { 22 | "type": "string", 23 | "description": "Docker project name" 24 | }, 25 | "DockerRepositoriesUrl": { 26 | "type": "string", 27 | "description": "Docker repositories url" 28 | }, 29 | "Help": { 30 | "type": "boolean", 31 | "description": "Shows the help text for this build assembly" 32 | }, 33 | "Host": { 34 | "type": "string", 35 | "description": "Host for execution. Default is 'automatic'", 36 | "enum": [ 37 | "AppVeyor", 38 | "AzurePipelines", 39 | "Bamboo", 40 | "Bitbucket", 41 | "Bitrise", 42 | "GitHubActions", 43 | "GitLab", 44 | "Jenkins", 45 | "Rider", 46 | "SpaceAutomation", 47 | "TeamCity", 48 | "Terminal", 49 | "TravisCI", 50 | "VisualStudio", 51 | "VSCode" 52 | ] 53 | }, 54 | "NoLogo": { 55 | "type": "boolean", 56 | "description": "Disables displaying the NUKE logo" 57 | }, 58 | "NuGetApiKey": { 59 | "type": "string", 60 | "description": "NuGet API key" 61 | }, 62 | "NuGetFeedName": { 63 | "type": "string", 64 | "description": "NuGet feed name" 65 | }, 66 | "NuGetUrl": { 67 | "type": "string", 68 | "description": "NuGet url" 69 | }, 70 | "Partition": { 71 | "type": "string", 72 | "description": "Partition to use on CI" 73 | }, 74 | "Plan": { 75 | "type": "boolean", 76 | "description": "Shows the execution plan (HTML)" 77 | }, 78 | "Profile": { 79 | "type": "array", 80 | "description": "Defines the profiles to load", 81 | "items": { 82 | "type": "string" 83 | } 84 | }, 85 | "PullRequestNumber": { 86 | "type": "string", 87 | "description": "Pull request number" 88 | }, 89 | "PullRequestSourceBranch": { 90 | "type": "string", 91 | "description": "Pull request source branch" 92 | }, 93 | "Root": { 94 | "type": "string", 95 | "description": "Root directory during build execution" 96 | }, 97 | "Skip": { 98 | "type": "array", 99 | "description": "List of targets to be skipped. Empty list skips all dependencies", 100 | "items": { 101 | "type": "string", 102 | "enum": [ 103 | "BuildDockerfile", 104 | "CreateRelease", 105 | "Default", 106 | "PushDockerArtifacts", 107 | "PushNuGetArtifacts" 108 | ] 109 | } 110 | }, 111 | "Target": { 112 | "type": "array", 113 | "description": "List of targets to be invoked. Default is '{default_target}'", 114 | "items": { 115 | "type": "string", 116 | "enum": [ 117 | "BuildDockerfile", 118 | "CreateRelease", 119 | "Default", 120 | "PushDockerArtifacts", 121 | "PushNuGetArtifacts" 122 | ] 123 | } 124 | }, 125 | "VcsNumber": { 126 | "type": "string", 127 | "description": "Vcs number" 128 | }, 129 | "Verbosity": { 130 | "type": "string", 131 | "description": "Logging verbosity during build execution. Default is 'Normal'", 132 | "enum": [ 133 | "Minimal", 134 | "Normal", 135 | "Quiet", 136 | "Verbose" 137 | ] 138 | } 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "Buldac.sln" 4 | } 5 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/Buldac.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buldac", "src\Buldac\Buldac.csproj", "{DBFE8D7A-6107-4600-AF3D-887E9B53349F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buldac.Tests", "tests\Buldac.Tests\Buldac.Tests.csproj", "{C678C548-A696-4C09-9DD1-7B1036AB1EE2}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "build\Build.csproj", "{A40EDE78-EE53-4D2E-9FCF-72AED2859FC9}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build.Common", "share\Build.Common\Build.Common.csproj", "{289763C4-5F31-4B1B-9E1C-4D16ACA77934}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {A40EDE78-EE53-4D2E-9FCF-72AED2859FC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {A40EDE78-EE53-4D2E-9FCF-72AED2859FC9}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {289763C4-5F31-4B1B-9E1C-4D16ACA77934}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {289763C4-5F31-4B1B-9E1C-4D16ACA77934}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {289763C4-5F31-4B1B-9E1C-4D16ACA77934}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {289763C4-5F31-4B1B-9E1C-4D16ACA77934}.Release|Any CPU.Build.0 = Release|Any CPU 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\Build.csproj" 17 | $TempDirectory = "$PSScriptRoot\..\..\..\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\..\..\..\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "STS" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function ExecSafe([scriptblock] $cmd) { 32 | & $cmd 33 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 34 | } 35 | 36 | # If dotnet CLI is installed globally and it matches requested version, use for execution 37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 40 | } 41 | else { 42 | # Download install script 43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 47 | 48 | # If global.json exists, load expected version 49 | if (Test-Path $DotNetGlobalFile) { 50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 52 | $DotNetVersion = $DotNetGlobal.sdk.version 53 | } 54 | } 55 | 56 | # Install by channel or version 57 | $DotNetDirectory = "$TempDirectory\dotnet-win" 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { 69 | & $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null 70 | & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null 71 | } 72 | 73 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 74 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 75 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/Build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR/../../../.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR/../../../global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="STS" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | export DOTNET_MULTILEVEL_LOOKUP=0 22 | 23 | ########################################################################### 24 | # EXECUTION 25 | ########################################################################### 26 | 27 | function FirstJsonValue { 28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 29 | } 30 | 31 | # If dotnet CLI is installed globally and it matches requested version, use for execution 32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 33 | export DOTNET_EXE="$(command -v dotnet)" 34 | else 35 | # Download install script 36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 37 | mkdir -p "$TEMP_DIRECTORY" 38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 39 | chmod +x "$DOTNET_INSTALL_FILE" 40 | 41 | # If global.json exists, load expected version 42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 44 | if [[ "$DOTNET_VERSION" == "" ]]; then 45 | unset DOTNET_VERSION 46 | fi 47 | fi 48 | 49 | # Install by channel or version 50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 51 | if [[ -z ${DOTNET_VERSION+x} ]]; then 52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 53 | else 54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 55 | fi 56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 57 | fi 58 | 59 | echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" 60 | 61 | if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "$NUKE_ENTERPRISE_TOKEN" != "" ]]; then 62 | "$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true 63 | "$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true 64 | fi 65 | 66 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 67 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 68 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_style_qualification_for_field = false:warning 3 | dotnet_style_qualification_for_property = false:warning 4 | dotnet_style_qualification_for_method = false:warning 5 | dotnet_style_qualification_for_event = false:warning 6 | dotnet_style_require_accessibility_modifiers = never:warning 7 | 8 | csharp_style_expression_bodied_methods = true:silent 9 | csharp_style_expression_bodied_properties = true:warning 10 | csharp_style_expression_bodied_indexers = true:warning 11 | csharp_style_expression_bodied_accessors = true:warning 12 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Build.cs: -------------------------------------------------------------------------------- 1 | using Build.Common; 2 | using Nuke.Common; 3 | 4 | class AppBuild : NukeBuild, IDefaultBuild 5 | { 6 | public string ServiceName => "Buldac"; 7 | 8 | public int Major => 0; 9 | 10 | public int Minor => 42; 11 | 12 | public static int Main() 13 | => Execute(x => ((IDefaultBuild)x).Default); 14 | } 15 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | CS0649;CS0169;CA1050;CA1822;CA2211;IDE1006 8 | .. 9 | .. 10 | 1 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | DO_NOT_SHOW 7 | Implicit 8 | Implicit 9 | ExpressionBody 10 | 0 11 | NEXT_LINE 12 | True 13 | False 14 | 120 15 | IF_OWNER_IS_SINGLE_LINE 16 | WRAP_IF_LONG 17 | False 18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 19 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | True 27 | True 28 | True 29 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using Nuke.Common.Tooling; 5 | 6 | [TypeConverter(typeof(TypeConverter))] 7 | public class Configuration : Enumeration 8 | { 9 | public static Configuration Debug = new Configuration { Value = nameof(Debug) }; 10 | public static Configuration Release = new Configuration { Value = nameof(Release) }; 11 | 12 | public static implicit operator string(Configuration configuration) 13 | { 14 | return configuration.Value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/build/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG REPO=mcr.microsoft.com/dotnet 2 | ARG SDK=${REPO}/sdk:6.0 3 | ARG RUN=${REPO}/aspnet:6.0 4 | 5 | ##################### Installer ##################### 6 | 7 | FROM ${RUN} AS installer 8 | 9 | ENV ASPNETCORE_URLS=http://*:8080 10 | EXPOSE 8080 11 | 12 | RUN groupadd --gid 999 dotnet && \ 13 | useradd --create-home --home-dir /app --uid 999 --gid dotnet dotnet 14 | 15 | USER 999:999 16 | 17 | ##################### Build ##################### 18 | 19 | FROM ${SDK} AS build 20 | 21 | ARG Version=0.0.0.0 22 | ARG AssemblyVersion=0.0.0.0 23 | ARG FileVersion=0.0.0.0 24 | ARG InformationalVersion=0.0.0.0 25 | 26 | # Enable integration with TeamCity (in tests) 27 | ENV TEAMCITY_VERSION=0 28 | 29 | RUN echo Application Version: ${Version} 30 | 31 | WORKDIR /app/src 32 | COPY . . 33 | 34 | RUN dotnet restore 35 | 36 | RUN dotnet build \ 37 | --configuration Release \ 38 | --no-restore \ 39 | --nologo \ 40 | -p:Version="${Version}" \ 41 | -p:AssemblyVersion="${AssemblyVersion}" \ 42 | -p:FileVersion="${FileVersion}" \ 43 | -p:InformationalVersion="${InformationalVersion}" 44 | 45 | ##################### Test ##################### 46 | 47 | FROM build AS test 48 | 49 | RUN dotnet test \ 50 | --filter "Category!=integration" \ 51 | --configuration Release \ 52 | # xunit/issues/1706 53 | --verbosity normal \ 54 | --no-build \ 55 | --no-restore \ 56 | --nologo 57 | # TODO: Add coverlet.collector to projects 58 | # --collect:"XPlat code coverage" 59 | 60 | ##################### Publish ##################### 61 | 62 | FROM test AS publish 63 | 64 | RUN dotnet pack \ 65 | --configuration Release \ 66 | --output "/app/artifacts/nuget" \ 67 | --no-build \ 68 | --no-restore \ 69 | --nologo \ 70 | -p:PackageVersion="${Version}" 71 | 72 | RUN dotnet publish \ 73 | "./src/Buldac/Buldac.csproj" \ 74 | --configuration Release \ 75 | --output "/app/bin" \ 76 | --no-build \ 77 | --no-restore \ 78 | --nologo 79 | 80 | ##################### Final ##################### 81 | 82 | FROM installer AS final 83 | 84 | WORKDIR /app 85 | COPY --from=publish /app/bin . 86 | 87 | # TODO: Remove artifacts from Final image 88 | COPY --from=publish /app/artifacts /app/artifacts 89 | 90 | ENTRYPOINT ["dotnet", "Buldac.dll"] 91 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Build.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/IBaseBuild.cs: -------------------------------------------------------------------------------- 1 | using Build.Common.Integration; 2 | using Nuke.Common; 3 | using Nuke.Common.IO; 4 | 5 | namespace Build.Common; 6 | 7 | public interface IBaseBuild : INukeBuild 8 | { 9 | string ServiceName { get; } 10 | 11 | int Major { get; } 12 | 13 | int Minor { get; } 14 | 15 | [Parameter("Build counter"), Required] 16 | string BuildCounter => this.GetValue(() => BuildCounter); 17 | 18 | [Parameter("Vcs number"), Required] 19 | string VcsNumber => this.GetValue(() => VcsNumber); 20 | 21 | [Parameter("Branch name"), Required] 22 | string Branch => this.GetValue(() => Branch); 23 | 24 | [Parameter("Pull request number"), Required] 25 | string PullRequestNumber => this.GetValue(() => PullRequestNumber); 26 | 27 | [Parameter("Pull request source branch"), Required] 28 | string PullRequestSourceBranch => this.GetValue(() => PullRequestSourceBranch); 29 | 30 | AbsolutePath ArtifactsPath => RootDirectory / ".artifacts"; 31 | } 32 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/IDefaultBuild.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | 3 | namespace Build.Common; 4 | 5 | /// 6 | /// Default build flow 7 | /// 8 | public interface IDefaultBuild : 9 | IDockerBuild, 10 | INuGetBuild, 11 | IReleaseBuild 12 | { 13 | Target Default => _ => _ 14 | .DependsOn(x => x.CreateRelease); 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/IDockerBuild.cs: -------------------------------------------------------------------------------- 1 | using Build.Common.Integration; 2 | using Nuke.Common; 3 | using Serilog; 4 | using static Nuke.Common.Tools.Docker.DockerTasks; 5 | 6 | namespace Build.Common; 7 | 8 | [ParameterPrefix(nameof(Docker))] 9 | public interface IDockerBuild : IBaseBuild 10 | { 11 | const string DockerContainerArtifactsPath = "/app/artifacts"; 12 | 13 | string DockerImageName => ServiceName.ToKebabCaseLower(); 14 | 15 | string DockerfilePath => RootDirectory / "build" / "Dockerfile"; 16 | 17 | [Parameter("Docker repositories url"), Required] 18 | Uri RepositoriesUrl => this.GetValue(() => RepositoriesUrl); 19 | 20 | [Parameter("Docker project name"), Required] 21 | string ProjectName => this.GetValue(() => ProjectName); 22 | 23 | string ResolveDockerImageNameTag() 24 | { 25 | var version = this.ResolveVersion(); 26 | return $"{RepositoriesUrl.Authority}/{ProjectName}/{DockerImageName}:{version.Version}"; 27 | } 28 | 29 | /// 30 | /// Dockerfile processing pipeline: build -> create container -> copy artifacts -> remove container 31 | /// 32 | Target BuildDockerfile => _ => _ 33 | .Requires(() => RepositoriesUrl) 34 | .Requires(() => ProjectName) 35 | .Executes(() => 36 | { 37 | SetupLogging(); 38 | 39 | this.BuildDocker(); 40 | 41 | var containerId = this.CreateDockerContainer(); 42 | this.CopyArtifactsFromContainer(containerId); 43 | 44 | this.RemoveDockerContainer(containerId); 45 | }); 46 | 47 | /// 48 | /// Push Docker image to the repository 49 | /// 50 | Target PushDockerArtifacts => _ => _ 51 | .Requires(() => RepositoriesUrl) 52 | .Requires(() => ProjectName) 53 | .TryDependsOn(x => x.BuildDockerfile) 54 | .Executes(() => 55 | { 56 | SetupLogging(); 57 | 58 | var imageTag = ResolveDockerImageNameTag(); 59 | // DockerPush(settings => settings 60 | // .SetName(imageTag); 61 | Log.Information("Docker image {DockerImageName} was pushed to {DockerImageTag}", DockerImageName, imageTag); 62 | }); 63 | 64 | // Workaround for logging issue in Nuke with Docker tasks 65 | // See more details here: https://nuke.build/faq 66 | static void SetupLogging() => DockerLogger = (_, text) => Log.Debug(text); 67 | } 68 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/INuGetBuild.cs: -------------------------------------------------------------------------------- 1 | using Build.Common.Integration; 2 | using Nuke.Common; 3 | using Nuke.Common.IO; 4 | using Serilog; 5 | 6 | namespace Build.Common; 7 | 8 | [ParameterPrefix(nameof(NuGet))] 9 | public interface INuGetBuild: IBaseBuild 10 | { 11 | [Parameter("NuGet url"), Required] 12 | Uri Url => this.GetValue(() => Url); 13 | 14 | [Parameter("NuGet feed name"), Required] 15 | string FeedName => this.GetValue(() => FeedName); 16 | 17 | //[Secret] 18 | [Parameter("NuGet API key"), Required] 19 | string ApiKey => this.GetValue(() => ApiKey); 20 | 21 | AbsolutePath NuGetArtifactsPath => ArtifactsPath / "nuget"; 22 | 23 | Target PushNuGetArtifacts => _ => _ 24 | .Requires(() => Url) 25 | .Requires(() => FeedName) 26 | .Requires(() => ApiKey) 27 | .TryDependsOn(x => x.BuildDockerfile) 28 | .Executes(() => 29 | { 30 | var nuGetPushUrl = Url.Combine($"nuget/{FeedName}/packages"); 31 | 32 | // DotNetNuGetPush(settings => 33 | // settings 34 | // .SetTargetPath(NuGetArtifactsPath / "*.nupkg") 35 | // .SetSource(nuGetPushUrl.OriginalString) 36 | // .SetApiKey(ApiKey) 37 | // .EnableSkipDuplicate() 38 | // .EnableForceEnglishOutput()); 39 | 40 | var pushedArtifacts = NuGetArtifactsPath.GlobFiles("*.nupkg") 41 | .Select(x => x.Name) 42 | .ToList(); 43 | 44 | Log.Information("Nuget artifacts {NuGetArtifacts} were successfully pushed to {NuGetUrl}", pushedArtifacts, nuGetPushUrl); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/IReleaseBuild.cs: -------------------------------------------------------------------------------- 1 | using Build.Common.Integration; 2 | using Nuke.Common; 3 | using Serilog; 4 | 5 | namespace Build.Common; 6 | 7 | public interface IReleaseBuild : IBaseBuild 8 | { 9 | Target CreateRelease => _ => _ 10 | .TryDependsOn(x => x.PushDockerArtifacts) 11 | .TryDependsOn(x => x.PushNuGetArtifacts) 12 | .Executes(() => 13 | { 14 | // Put here any logic you need to create release using your CI/CD system API 15 | 16 | var version = this.ResolveVersion(); 17 | Log.Information("Release {ReleaseName} was successfully created for service {ServiceName}",version.Version, ServiceName); 18 | }); 19 | } -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Integration/ApplicationVersion.cs: -------------------------------------------------------------------------------- 1 | namespace Build.Common.Integration; 2 | 3 | internal record ApplicationVersion( 4 | string AssemblyVersion, 5 | string Version, 6 | string FileVersion, 7 | string InformationalVersion); -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Integration/BaseBuildExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Build.Common.Integration; 5 | 6 | internal static class BaseBuildExtensions 7 | { 8 | private static readonly Regex PreReleaseSuffixEscaper = new("[^0-9A-Za-z-]", RegexOptions.Compiled | RegexOptions.CultureInvariant); 9 | 10 | public static T GetValue(this IBaseBuild build, Expression> parameterExpression) 11 | where T : class 12 | => build.TryGetValue(parameterExpression) 13 | ?? throw new InvalidOperationException($"Cannot get value for {parameterExpression}"); 14 | 15 | public static PullRequest? ResolvePullRequest(this IBaseBuild build) 16 | { 17 | var number = build.PullRequestNumber; 18 | var branch = build.PullRequestSourceBranch; 19 | 20 | // TeamCity can't replace parameters if they don't exist 21 | if (number.Contains('%')) 22 | { 23 | // This is not a pull request 24 | return default; 25 | } 26 | 27 | // Sometimes TeamCity can't provide the name of the source branch 28 | if (branch.Contains('%')) 29 | { 30 | branch = "merge"; 31 | } 32 | 33 | return new(number, branch); 34 | } 35 | 36 | public static string FormatPreReleaseSuffix(this IBaseBuild build) 37 | { 38 | var name = build.Branch; 39 | if (name == "master") 40 | { 41 | // Pre-release suffix not required 42 | return String.Empty; 43 | } 44 | 45 | var pullRequest = build.ResolvePullRequest(); 46 | if (pullRequest != null) 47 | { 48 | name = $"{pullRequest.SourceBranchName}-pr{pullRequest.Number}"; 49 | } 50 | 51 | var suffix = PreReleaseSuffixEscaper.Replace(name, "-"); 52 | if (suffix.Length == 0 || !Char.IsLetter(suffix[0])) 53 | { 54 | suffix = "pre" + suffix; 55 | } 56 | 57 | return $"-{suffix}"; 58 | } 59 | 60 | public static ApplicationVersion ResolveVersion(this IBaseBuild build) 61 | { 62 | var major = build.Major; 63 | var minor = build.Minor; 64 | var patch = build.BuildCounter; 65 | var preReleaseSuffix = build.FormatPreReleaseSuffix(); 66 | 67 | return new( 68 | AssemblyVersion: $"{major}.{minor}", 69 | Version: $"{major}.{minor}.{patch}{preReleaseSuffix}", 70 | FileVersion: $"{major}.{minor}.{patch}", 71 | InformationalVersion: $"{major}.{minor}.{patch}{preReleaseSuffix}+commit.${build.VcsNumber}"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Integration/DockerBuildExtensions.cs: -------------------------------------------------------------------------------- 1 | using Nuke.Common; 2 | using Nuke.Common.Tooling; 3 | using Nuke.Common.Tools.Docker; 4 | using Serilog; 5 | using static Nuke.Common.Tools.Docker.DockerTasks; 6 | 7 | namespace Build.Common.Integration; 8 | 9 | internal static class DockerBuildExtensions 10 | { 11 | public static void BuildDocker(this IDockerBuild build) 12 | { 13 | Log.Information("Building docker image {DockerImageName}", build.DockerImageName); 14 | 15 | var version = build.ResolveVersion(); 16 | 17 | DockerBuild(settings => settings 18 | .SetPath(build.RootDirectory) 19 | .SetFile(build.DockerfilePath) 20 | .SetTag(build.ResolveDockerImageNameTag()) 21 | .EnablePull() 22 | .SetProgress(ProgressType.plain) 23 | .SetTarget("final") 24 | .SetBuildArg( 25 | $"Version={version.Version}", 26 | $"AssemblyVersion={version.AssemblyVersion}", 27 | $"FileVersion={version.FileVersion}", 28 | $"InformationalVersion={version.InformationalVersion}")); 29 | } 30 | 31 | public static string CreateDockerContainer(this IDockerBuild build) 32 | { 33 | Log.Information("Creating Docker container for {DockerImageName}", build.DockerImageName); 34 | 35 | var createResult = DockerContainerCreate(settings => settings 36 | .SetImage(build.ResolveDockerImageNameTag())); 37 | 38 | Assert.Count(createResult, 1); 39 | Assert.True(createResult.Single().Type == OutputType.Std); 40 | var containerId = createResult.Single().Text; 41 | containerId.NotNullOrWhiteSpace(); 42 | 43 | return containerId; 44 | } 45 | 46 | public static void CopyArtifactsFromContainer(this IDockerBuild build, string containerId) 47 | { 48 | Log.Information("Copying items from Docker container {ContainerId}", containerId); 49 | 50 | var source = $"{IDockerBuild.DockerContainerArtifactsPath}/."; 51 | var destination = build.ArtifactsPath; 52 | 53 | var containerSource = $"{containerId}:{source}"; 54 | Docker($"container cp {containerSource} {destination}"); 55 | } 56 | 57 | public static void RemoveDockerContainer(this IDockerBuild build, string containerId) 58 | { 59 | Log.Information("Removing Docker container {ContainerId}", containerId); 60 | 61 | var removeResult = DockerContainerRm(settings => settings 62 | .SetContainers(containerId) 63 | .EnableForce()); 64 | 65 | Assert.Count(removeResult, 1); 66 | Assert.True(String.Equals(removeResult.Single().Text, containerId, StringComparison.OrdinalIgnoreCase)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Integration/PullRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Build.Common.Integration; 2 | 3 | internal record PullRequest( 4 | string Number, 5 | string SourceBranchName); -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/share/Build.Common/Integration/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace Build.Common.Integration; 4 | 5 | internal static class StringExtensions 6 | { 7 | private static readonly Regex PascalToKebabCaseRegex = new("(? 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace Buldac.Controllers; 4 | 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class WeatherForecastController : ControllerBase 8 | { 9 | private static readonly string[] Summaries = new[] 10 | { 11 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 12 | }; 13 | 14 | private readonly ILogger _logger; 15 | 16 | public WeatherForecastController(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | [HttpGet(Name = "GetWeatherForecast")] 22 | public IEnumerable Get() 23 | { 24 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 25 | { 26 | Date = DateTime.Now.AddDays(index), 27 | TemperatureC = Random.Shared.Next(-20, 55), 28 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 29 | }) 30 | .ToArray(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Add services to the container. 4 | 5 | builder.Services.AddControllers(); 6 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 7 | builder.Services.AddEndpointsApiExplorer(); 8 | builder.Services.AddSwaggerGen(); 9 | 10 | var app = builder.Build(); 11 | 12 | // Configure the HTTP request pipeline. 13 | if (app.Environment.IsDevelopment()) 14 | { 15 | app.UseSwagger(); 16 | app.UseSwaggerUI(); 17 | } 18 | 19 | app.UseHttpsRedirection(); 20 | 21 | app.UseAuthorization(); 22 | 23 | app.MapControllers(); 24 | 25 | app.Run(); 26 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:38644", 8 | "sslPort": 44369 9 | } 10 | }, 11 | "profiles": { 12 | "Buldac": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7198;http://localhost:5013", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace Buldac; 2 | 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string? Summary { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/src/Buldac/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/tests/Buldac.Tests/Buldac.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/Nuke/tests/Buldac.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Buldac.Tests; 4 | 5 | public class UnitTest1 6 | { 7 | [Fact] 8 | public void Test1() 9 | { 10 | Assert.True(true); 11 | } 12 | } -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/Buldac.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buldac", "src\Buldac\Buldac.csproj", "{DBFE8D7A-6107-4600-AF3D-887E9B53349F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buldac.Tests", "tests\Buldac.Tests\Buldac.Tests.csproj", "{C678C548-A696-4C09-9DD1-7B1036AB1EE2}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(SolutionProperties) = preSolution 16 | HideSolutionNode = FALSE 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {DBFE8D7A-6107-4600-AF3D-887E9B53349F}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {C678C548-A696-4C09-9DD1-7B1036AB1EE2}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/build.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5 2 | 3 | <# 4 | TeamCity arguments: 5 | -Counter %build.counter% 6 | -VcsNumber "%build.vcs.number%" 7 | -Branch "%teamcity.build.branch%" 8 | -PullRequestNumber "%teamcity.pullRequest.number%" 9 | -PullRequestSourceBranch "%teamcity.pullRequest.source.branch%" 10 | -DockerRepositoriesUrl "%DockerRepositoriesUrl%" 11 | -DockerProjectName "%DockerProjectName%" 12 | -NuGetUrl "%NuGetUrl%" 13 | -NuGetFeedName "%NuGetFeedName%" 14 | -NuGetKey "%NNuGetKey%" 15 | -OctopusProjectsUrl "%OctopusProjectsUrl%" 16 | -OctopusKey "%OctopusKey%" 17 | -DotNetToolUrl "%DotNetToolUrl%" 18 | #> 19 | 20 | [CmdletBinding()] 21 | param ( 22 | [int] 23 | [Parameter(Mandatory)] 24 | [ValidateNotNullOrEmpty()] 25 | $Counter, 26 | 27 | [string] 28 | [Parameter(Mandatory)] 29 | [ValidateNotNullOrEmpty()] 30 | $VcsNumber, 31 | 32 | [string] 33 | [Parameter(Mandatory)] 34 | [ValidateNotNullOrEmpty()] 35 | $Branch, 36 | 37 | [string] 38 | [Parameter()] 39 | [AllowNull()] 40 | [AllowEmptyString()] 41 | $PullRequestNumber = $null, 42 | 43 | [string] 44 | [Parameter()] 45 | [AllowNull()] 46 | [AllowEmptyString()] 47 | $PullRequestSourceBranch = $null, 48 | 49 | [Uri] 50 | [Parameter(Mandatory)] 51 | [ValidateNotNullOrEmpty()] 52 | $DockerRepositoriesUrl, 53 | 54 | [string] 55 | [Parameter(Mandatory)] 56 | [ValidateNotNullOrEmpty()] 57 | $DockerProjectName, 58 | 59 | [Uri] 60 | [Parameter(Mandatory)] 61 | [ValidateNotNullOrEmpty()] 62 | $NuGetUrl, 63 | 64 | [string] 65 | [Parameter(Mandatory)] 66 | [ValidateNotNullOrEmpty()] 67 | $NuGetFeedName, 68 | 69 | [string] 70 | [Parameter(Mandatory)] 71 | [ValidateNotNullOrEmpty()] 72 | $NuGetKey, 73 | 74 | [Uri] 75 | [Parameter(Mandatory)] 76 | [ValidateNotNullOrEmpty()] 77 | $OctopusProjectsUrl, 78 | 79 | [string] 80 | [Parameter(Mandatory)] 81 | [ValidateNotNullOrEmpty()] 82 | $OctopusKey, 83 | 84 | [Uri] 85 | [Parameter(Mandatory)] 86 | [ValidateNotNullOrEmpty()] 87 | $DotNetToolUrl, 88 | 89 | [Parameter(ValueFromRemainingArguments)] 90 | $UnboundArguments 91 | ) 92 | 93 | Set-StrictMode -version Latest 94 | $ErrorActionPreference = 'Stop' 95 | $InformationPreference = 'Continue' 96 | 97 | . $PSScriptRoot/build/TeamCity.ps1 98 | . $PSScriptRoot/build/Builder.ps1 99 | 100 | ##################### Specification ##################### 101 | 102 | $ServiceName = "Buldac" 103 | $ServiceMainProjectPath = "./src/Buldac/Buldac.csproj" 104 | $ServiceMainAssemblyName = "Buldac.dll" 105 | 106 | $DockerImageName = $ServiceName.ToLower() 107 | $OctopusProjectName = $ServiceName 108 | 109 | ##################### Variables ##################### 110 | 111 | $ArtifactsPath = Join-Path $PSScriptRoot 'artifacts' 112 | 113 | $DockerfilePath = Join-Path $PSScriptRoot 'build' | Join-Path -ChildPath 'Dockerfile' 114 | # See Dockerfile, Publish and Final stages 115 | $DockerContainerArtifactsPath = '/app/artifacts' 116 | $DockerArtifactsPath = Join-Path $ArtifactsPath 'docker' 117 | 118 | $NuGetArtifactsPath = Join-Path $ArtifactsPath 'nuget' 119 | $NuGetPushUrl = $NuGetUrl | Join-Uri "nuget/${NuGetFeedName}/packages" 120 | 121 | $OctopusArtifactsPath = Join-Path $ArtifactsPath 'octopus' 122 | $OctopusUrl = [Uri]$OctopusProjectsUrl.GetLeftPart('Authority') 123 | 124 | $PullRequestNumber, $PullRequestSourceBranch = Resolve-PullRequestLabels ` 125 | -PullRequestNumber $PullRequestNumber ` 126 | -PullRequestSourceBranch $PullRequestSourceBranch 127 | 128 | ##################### Version ##################### 129 | 130 | $major = 1 131 | $minor = 2 132 | $patch = $Counter 133 | 134 | $preRelease = Format-PreReleaseSuffix ` 135 | -Branch $Branch ` 136 | -PullRequestNumber $PullRequestNumber ` 137 | -PullRequestSourceBranch $PullRequestSourceBranch 138 | 139 | $assemblyVersion = "${major}.${minor}" 140 | $version = "${major}.${minor}.${patch}${preRelease}" 141 | $fileVersion = "${major}.${minor}.${patch}" 142 | $informationalVersion = "${version}+commit.${VcsNumber}" 143 | 144 | $DockerImageNameTag = "$($DockerRepositoriesUrl.Authority)/${DockerProjectName}/${DockerImageName}:${version}" 145 | 146 | Set-TeamCityBuildNumber -Number $version 147 | 148 | Write-Information "Detailed Version: $informationalVersion" 149 | Write-Information "Docker image name: $DockerImageNameTag" 150 | 151 | $isPullRequest = [String]::IsNullOrWhiteSpace($PullRequestNumber) -eq $false 152 | 153 | ##################### Steps ##################### 154 | 155 | Step 'Build Dockerfile' { 156 | 157 | $ServiceMainProjectPath, $ServiceMainAssemblyName | Test-DockerfileContent -Path "$DockerfilePath" 158 | 159 | docker build ` 160 | --tag "$DockerImageNameTag" ` 161 | --pull ` 162 | --progress plain ` 163 | --file "$DockerfilePath" ` 164 | --target final ` 165 | --build-arg Version="$version" ` 166 | --build-arg AssemblyVersion="$assemblyVersion" ` 167 | --build-arg FileVersion="$fileVersion" ` 168 | --build-arg InformationalVersion="$informationalVersion" ` 169 | . 170 | 171 | Test-ExitCode 'docker build' 172 | 173 | Copy-ItemsFromDockerImage ` 174 | -ImageNameTag "$DockerImageNameTag" ` 175 | -Source "$DockerContainerArtifactsPath/." ` 176 | -Destination "$ArtifactsPath" 177 | } 178 | 179 | Step 'Push Docker artifacts' -SkipIf $isPullRequest { 180 | 181 | docker push "$DockerImageNameTag" 182 | Test-ExitCode 'docker push' 183 | 184 | Out-DockerRedirectFile -Destination "$DockerArtifactsPath" -RepositoriesUrl "$DockerRepositoriesUrl" -ImageName "$DockerImageName" -Version "$version" 185 | Publish-TeamCityArtifact -Pattern "+:$(Join-Path $DockerArtifactsPath '*.html') => Docker" 186 | } 187 | 188 | Step 'Push NuGet artifacts' -SkipIf $isPullRequest { 189 | 190 | $packageFilter = Join-Path "$NuGetArtifactsPath" '*.nupkg' 191 | 192 | dotnet nuget push ` 193 | "$packageFilter" ` 194 | --source "$NuGetPushUrl" ` 195 | --api-key "$NuGetKey" ` 196 | --skip-duplicate ` 197 | --force-english-output ` 198 | 199 | Test-ExitCode 'nuget push' 200 | 201 | Get-ChildItem -Path $packageFilter | 202 | Out-NuGetRedirectFile -Url $NuGetUrl -FeedName $NuGetFeedName 203 | Publish-TeamCityArtifact -Pattern "+:$(Join-Path $NuGetArtifactsPath '*.html') => NuGet" 204 | } 205 | 206 | Step 'Create Octopus Release' -SkipIf $isPullRequest { 207 | 208 | New-OctopusRelease ` 209 | -ServerUrl "$OctopusUrl" ` 210 | -ApiKey "$OctopusKey" ` 211 | -ProjectName "$OctopusProjectName" ` 212 | -Version "$version" ` 213 | -DotNetToolUrl "$DotNetToolUrl" 214 | 215 | Out-OctopusRedirectFile -Destination "$OctopusArtifactsPath" -ProjectsUrl "$OctopusProjectsUrl" -ProjectName "$OctopusProjectName" -Version "$version" 216 | Publish-TeamCityArtifact -Pattern "+:$(Join-Path $OctopusArtifactsPath '*.html') => Octopus" 217 | } 218 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/build/Builder.ps1: -------------------------------------------------------------------------------- 1 | function Format-PreReleaseSuffix( 2 | [string] $Branch = $(throw 'Branch Name required'), 3 | [string] $PullRequestNumber = $(throw 'Pull Request Number required'), 4 | [string] $PullRequestSourceBranch = $(throw 'Pull Request Source Branch required')) 5 | { 6 | if ($Branch -eq 'master') 7 | { 8 | # pre-release suffix not required 9 | return '' 10 | } 11 | 12 | $name = $Branch 13 | $isPullRequest = [String]::IsNullOrWhiteSpace($PullRequestNumber) -eq $false 14 | if ($isPullRequest) 15 | { 16 | $name = "${PullRequestSourceBranch}-pr${PullRequestNumber}" 17 | } 18 | 19 | $suffix = $name -replace '[^0-9A-Za-z-]','-' 20 | 21 | if (($suffix.Length -eq 0) -or (-not [Char]::IsLetter($suffix[0]))) 22 | { 23 | $suffix = 'pre' + $suffix 24 | } 25 | 26 | return "-$suffix" 27 | } 28 | 29 | function Resolve-PullRequestLabels( 30 | [string] $PullRequestNumber = $(throw 'Pull Request Number required'), 31 | [string] $PullRequestSourceBranch = $(throw 'Pull Request Source Branch required')) 32 | { 33 | # TeamCity can't replace parameters if they don't exist 34 | if ($PullRequestNumber.Contains('%')) 35 | { 36 | # This is not a pull request 37 | return $null, $null 38 | } 39 | 40 | # Sometimes TeamCity can't provide the name of the source branch 41 | if ($PullRequestSourceBranch.Contains('%')) 42 | { 43 | $PullRequestSourceBranch = 'merge' 44 | } 45 | 46 | return $PullRequestNumber, $PullRequestSourceBranch 47 | } 48 | 49 | function Reset-Directory() 50 | { 51 | process 52 | { 53 | $path = $_ 54 | if (Test-Path -Path $path -PathType 'Container') 55 | { 56 | Remove-Item -Path $path -Recurse -Force 57 | } 58 | 59 | New-Item -ItemType Directory -Path $path -Force | Out-Null 60 | } 61 | } 62 | 63 | function Join-Uri([string] $RelativeUri = $(throw 'Relative Uri required')) 64 | { 65 | process 66 | { 67 | $BaseUri = $_ 68 | $left = $BaseUri.ToString().TrimEnd('/') 69 | $right = $RelativeUri.TrimStart('/') 70 | $separator = '/' 71 | 72 | if ($left.Contains('?') -or $right.StartsWith('?')) 73 | { 74 | $separator = '' 75 | } 76 | 77 | [Uri] "${left}${separator}${right}" 78 | } 79 | } 80 | 81 | function Copy-ItemsFromDockerImage( 82 | [string] $ImageNameTag = $(throw "Docker Image Name Tag required"), 83 | [string] $Source = $(throw "Source required"), 84 | [string] $Destination = $(throw "Destination required")) 85 | { 86 | $Destination | Reset-Directory 87 | $containerId = docker container create "$ImageNameTag" 88 | Test-ExitCode 'docker create' 89 | $containerSource = "${containerId}:${Source}" 90 | 91 | docker container cp $containerSource $Destination 92 | Test-ExitCode 'docker cp' 93 | docker container rm --force $containerId 94 | } 95 | 96 | function New-OctopusRelease( 97 | [string] $ServerUrl = $(throw "Octopus server url required"), 98 | [string] $ApiKey = $(throw "Octopus API key required"), 99 | [string] $ProjectName = $(throw "Octopus project name required"), 100 | [string] $Version = $(throw "Version required"), 101 | [string] $DotNetToolUrl = $(throw "DotNet tool url required")) 102 | { 103 | $OctopusCliExists = dotnet tool list -g | Select-String '^octopus.dotnet.fortis.cli\s+' 104 | if (-not $OctopusCliExists) 105 | { 106 | dotnet tool install --global --add-source "$DotNetToolUrl" "Octopus.DotNet.Fortis.Cli" 107 | } 108 | 109 | dotnet-fortis-octo create-release ` 110 | --releaseNumber="$Version" ` 111 | --server="$ServerUrl" ` 112 | --apikey="$ApiKey" ` 113 | --project="$ProjectName" ` 114 | --latestbypublishdate ` 115 | --ignoreexisting 116 | 117 | Test-ExitCode 'octo release' 118 | } 119 | 120 | function Out-HtmlRedirectFile( 121 | [Uri] $Url = $(throw 'Url required'), 122 | [string] $FilePath = $(throw 'File Path required')) 123 | { 124 | @" 125 | 126 | 127 | 128 | "@ | 129 | Out-File -Encoding UTF8 -FilePath $FilePath 130 | } 131 | 132 | function Out-DockerRedirectFile( 133 | [string] $Destination = $(throw 'Destination path required'), 134 | [Uri] $RepositoriesUrl = $(throw 'Docker repositories url required'), 135 | [string] $ImageName = $(throw 'Image name required'), 136 | [string] $Version = $(throw 'Version required')) 137 | { 138 | $Destination | Reset-Directory 139 | $redirectUrl = $RepositoriesUrl | Join-Uri "${ImageName}/artifacts/${Version}" 140 | $htmlFilePath = Join-Path $Destination "${ImageName}-${Version}.html" 141 | Out-HtmlRedirectFile -Url $redirectUrl -FilePath $htmlFilePath 142 | } 143 | 144 | function Out-NuGetRedirectFile( 145 | [Uri] $Url = $(throw '$Url required'), 146 | [string] $FeedName = $(throw '$Feed Name required')) 147 | { 148 | begin 149 | { 150 | # Based on https://github.com/semver/semver/blob/master/semver.md#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string 151 | $semVerRegex = "^(?.*?).(?(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)\.nupkg$" 152 | } 153 | process 154 | { 155 | $packageFile = $_ 156 | if ($packageFile.Name -imatch $semVerRegex) 157 | { 158 | $packageName = $Matches.PackageName 159 | $packageVerison = $Matches.PackageVersion 160 | $redirectUrl = $Url | Join-Uri "feeds/${FeedName}/${packageName}/${packageVerison}" 161 | $htmlFilePath = "$($packageFile.FullName).html" 162 | Out-HtmlRedirectFile -Url $redirectUrl -FilePath $htmlFilePath 163 | } 164 | } 165 | } 166 | 167 | function Out-OctopusRedirectFile( 168 | [string] $Destination = $(throw 'Destination required'), 169 | [string] $ProjectsUrl = $(throw "Octopus projects url required"), 170 | [string] $ProjectName = $(throw "Octopus project name required"), 171 | [string] $Version = $(throw "Version required")) 172 | { 173 | $Destination | Reset-Directory 174 | $redirectUrl = $ProjectsUrl | Join-Uri "${ProjectName}/deployments/releases/${Version}" 175 | $htmlFilePath = Join-Path $Destination "${ProjectName}-${Version}.html" 176 | Out-HtmlRedirectFile -Url $redirectUrl -FilePath $htmlFilePath 177 | } 178 | 179 | function Test-DockerfileContent([string] $Path = $(throw 'Path required')) 180 | { 181 | begin 182 | { 183 | $hasError = $false 184 | } 185 | process 186 | { 187 | $pattern = $_ 188 | $contains = Select-String -Path $Path -Pattern $pattern 189 | if ($contains) 190 | { 191 | return 192 | } 193 | 194 | $hasError = $true 195 | Write-Warning "«${pattern}» pattern was not found in $Path. Please check and replace all service specific variables." 196 | } 197 | end 198 | { 199 | if ($hasError) 200 | { 201 | throw 'Please, update Dockerfile' 202 | } 203 | } 204 | } 205 | 206 | function Test-ExitCode([string] $CommandName = 'Command') 207 | { 208 | if ($LASTEXITCODE) 209 | { 210 | throw "«${CommandName}» as executed with an error (${LASTEXITCODE}). Please check the messages above." 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/build/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG REPO=mcr.microsoft.com/dotnet 2 | ARG SDK=${REPO}/sdk:6.0 3 | ARG RUN=${REPO}/aspnet:6.0 4 | 5 | ##################### Installer ##################### 6 | 7 | FROM ${RUN} AS installer 8 | 9 | ENV ASPNETCORE_URLS=http://*:8080 10 | EXPOSE 8080 11 | 12 | RUN groupadd --gid 999 dotnet && \ 13 | useradd --create-home --home-dir /app --uid 999 --gid dotnet dotnet 14 | 15 | USER 999:999 16 | 17 | ##################### Build ##################### 18 | 19 | FROM ${SDK} AS build 20 | 21 | ARG Version=0.0.0.0 22 | ARG AssemblyVersion=0.0.0.0 23 | ARG FileVersion=0.0.0.0 24 | ARG InformationalVersion=0.0.0.0 25 | 26 | # Enable integration with TeamCity (in tests) 27 | ENV TEAMCITY_VERSION=0 28 | 29 | RUN echo Application Version: ${Version} 30 | 31 | WORKDIR /app/src 32 | COPY . . 33 | 34 | RUN dotnet restore 35 | 36 | RUN dotnet build \ 37 | --configuration Release \ 38 | --no-restore \ 39 | --nologo \ 40 | -p:Version="${Version}" \ 41 | -p:AssemblyVersion="${AssemblyVersion}" \ 42 | -p:FileVersion="${FileVersion}" \ 43 | -p:InformationalVersion="${InformationalVersion}" 44 | 45 | ##################### Test ##################### 46 | 47 | FROM build AS test 48 | 49 | RUN dotnet test \ 50 | --filter "Category!=integration" \ 51 | --configuration Release \ 52 | # xunit/issues/1706 53 | --verbosity normal \ 54 | --no-build \ 55 | --no-restore \ 56 | --nologo 57 | # TODO: Add coverlet.collector to projects 58 | # --collect:"XPlat code coverage" 59 | 60 | ##################### Publish ##################### 61 | 62 | FROM test AS publish 63 | 64 | RUN dotnet pack \ 65 | --configuration Release \ 66 | --output "/app/artifacts/nuget" \ 67 | --no-build \ 68 | --no-restore \ 69 | --nologo \ 70 | -p:PackageVersion="${Version}" 71 | 72 | RUN dotnet publish \ 73 | "./src/Buldac/Buldac.csproj" \ 74 | --configuration Release \ 75 | --output "/app/bin" \ 76 | --no-build \ 77 | --no-restore \ 78 | --nologo 79 | 80 | ##################### Final ##################### 81 | 82 | FROM installer AS final 83 | 84 | WORKDIR /app 85 | COPY --from=publish /app/bin . 86 | 87 | # TODO: Remove artifacts from Final image 88 | COPY --from=publish /app/artifacts /app/artifacts 89 | 90 | ENTRYPOINT ["dotnet", "Buldac.dll"] 91 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/build/TeamCity.ps1: -------------------------------------------------------------------------------- 1 | function Format-TeamCityParameter([string] $Value = $(throw "Value required")) 2 | { 3 | # https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-Escapedvalues 4 | $Value = $Value.Replace('|', '||') 5 | $Value = $Value.Replace("'", "|'") 6 | $Value = $Value.Replace('\n', '|n') 7 | $Value = $Value.Replace('\r', '|r') 8 | $Value = $Value.Replace('[', '|[') 9 | $Value = $Value.Replace(']', '|]') 10 | $Value 11 | } 12 | 13 | function Set-TeamCityBuildNumber([string] $Number = $(throw "Number required")) 14 | { 15 | Write-Host "##teamcity[buildNumber '$Number']" 16 | } 17 | 18 | function Publish-TeamCityArtifact([string] $Pattern = $(throw "Pattern required")) 19 | { 20 | Write-Host "##teamcity[publishArtifacts '$Pattern']" 21 | } 22 | 23 | function Open-TeamCityBlock([string] $Name = $(throw "Name required"), [string] $Description = '') 24 | { 25 | $decodedName = Format-TeamCityParameter -Value $Name 26 | $decodedDescription = Format-TeamCityParameter -Value $Description 27 | Write-Host "##teamcity[blockOpened name='$decodedName' description='$decodedDescription']" 28 | } 29 | 30 | function Close-TeamCityBlock([string] $Name = $(throw "Name required")) 31 | { 32 | $decodedName = Format-TeamCityParameter -Value $Name 33 | Write-Host "##teamcity[blockClosed name='$decodedName']" 34 | } 35 | 36 | function Step { 37 | [CmdletBinding()] 38 | param ( 39 | [Parameter(Mandatory, Position = 0)] 40 | [ValidateNotNullOrEmpty()] 41 | [string] $Name, 42 | 43 | [Parameter(Mandatory, Position = 1)] 44 | [ValidateNotNull()] 45 | [scriptblock] $Action, 46 | 47 | [Parameter()] 48 | [bool] $SkipIf = $false 49 | ) 50 | 51 | if ($SkipIf) 52 | { 53 | Write-Information "Skip: $Name" 54 | return 55 | } 56 | 57 | Open-TeamCityBlock -Name $Name 58 | Invoke-Command -ScriptBlock $Action 59 | Close-TeamCityBlock $Name 60 | } 61 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/build/build-local.ps1: -------------------------------------------------------------------------------- 1 | # Run it from solution root folder 2 | ./build.ps1 ` 3 | -Counter 42 ` 4 | -VcsNumber 'a42b' ` 5 | -Branch 'test-local' ` 6 | -PullRequestNumber '24' ` 7 | -PullRequestSourceBranch 'local' ` 8 | -DockerRepositoriesUrl 'https://localhost/harbor/projects/1/repositories' ` 9 | -DockerProjectName 'library' ` 10 | -NuGetUrl 'https://localhost' ` 11 | -NuGetFeedName 'Nuget-Push' ` 12 | -NuGetKey 'null' ` 13 | -OctopusProjectsUrl 'https://localhost/app#/Spaces-1/projects' ` 14 | -OctopusKey 'null' ` 15 | -DotNetToolUrl 'https://localhost' 16 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/Buldac.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace Buldac.Controllers; 4 | 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class WeatherForecastController : ControllerBase 8 | { 9 | private static readonly string[] Summaries = new[] 10 | { 11 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 12 | }; 13 | 14 | private readonly ILogger _logger; 15 | 16 | public WeatherForecastController(ILogger logger) 17 | { 18 | _logger = logger; 19 | } 20 | 21 | [HttpGet(Name = "GetWeatherForecast")] 22 | public IEnumerable Get() 23 | { 24 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 25 | { 26 | Date = DateTime.Now.AddDays(index), 27 | TemperatureC = Random.Shared.Next(-20, 55), 28 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 29 | }) 30 | .ToArray(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Add services to the container. 4 | 5 | builder.Services.AddControllers(); 6 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 7 | builder.Services.AddEndpointsApiExplorer(); 8 | builder.Services.AddSwaggerGen(); 9 | 10 | var app = builder.Build(); 11 | 12 | // Configure the HTTP request pipeline. 13 | if (app.Environment.IsDevelopment()) 14 | { 15 | app.UseSwagger(); 16 | app.UseSwaggerUI(); 17 | } 18 | 19 | app.UseHttpsRedirection(); 20 | 21 | app.UseAuthorization(); 22 | 23 | app.MapControllers(); 24 | 25 | app.Run(); 26 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:38644", 8 | "sslPort": 44369 9 | } 10 | }, 11 | "profiles": { 12 | "Buldac": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7198;http://localhost:5013", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace Buldac; 2 | 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string? Summary { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/src/Buldac/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/tests/Buldac.Tests/Buldac.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BuildAsCode/Demo/PowerShell/tests/Buldac.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Buldac.Tests; 4 | 5 | public class UnitTest1 6 | { 7 | [Fact] 8 | public void Test1() 9 | { 10 | Assert.True(true); 11 | } 12 | } -------------------------------------------------------------------------------- /BuildAsCode/Demo/README.md: -------------------------------------------------------------------------------- 1 | # Примеры кода 2 | 3 | - [PowerShell](PowerShell) — пример сборки на основе Docker и PowerShell 4 | - [Nuke](Nuke) — пример на основе Docker и Nuke Build -------------------------------------------------------------------------------- /BuildAsCode/Images/Bender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/BuildAsCode/Images/Bender.png -------------------------------------------------------------------------------- /BuildAsCode/README.md: -------------------------------------------------------------------------------- 1 | # Build as Code 2 | 3 | введение в тему построения, тестирования и публикации приложений на подходе с контейнерной изоляцией и полностью описанном в коде на PowerShell или Nuke Build. 4 | 5 | ![My own CI](./Images/Bender.png) 6 | 7 | ## Выступления 8 | 9 | - 2023.09.16 — [DotNext 2023 Moscow](https://dotnext.ru/talks/6bf41f9f1d94439b8d7ba2233adbdaeb/) (Москва): [Слайды](../../dotnext2023msk/BuildAsCode/Build-as-Code.pdf), [Код](../../dotnext2023msk/BuildAsCode/Demo) 10 | - 2023.05.25 — [SpbDotNet №88](https://spbdotnet.timepad.ru/event/2433139/) (Санкт-Петербург): [Слайды](../../spbdotnet88/BuildAsCode/Build-as-Code.pdf), [Код](../../spbdotnet88/BuildAsCode/Demo), [Видео](https://www.youtube.com/watch?v=yaQsQvPwlvg) 11 | 12 | ## Ссылки 13 | 14 | - [docs.docker.com](https://docs.docker.com/) 15 | - [nuke.build](https://nuke.build/) 16 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/ArchBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using BenchmarkDotNet.Attributes; 4 | 5 | namespace ArchBenchmark 6 | { 7 | [InfluxExporter(InfluxServer, Measurement)] 8 | [Config(typeof(MyConfig))] 9 | public class ArchBenchmark 10 | { 11 | public const string InfluxServer = "http://127.0.0.1:8086"; 12 | public const string Measurement = "arch"; 13 | 14 | private readonly byte[] data; 15 | 16 | public ArchBenchmark() 17 | { 18 | data = new byte[1000 * 1000]; 19 | new Random().NextBytes(data); 20 | } 21 | 22 | [Benchmark] 23 | public byte[] PackFast() 24 | { 25 | using (var md5 = MD5.Create()) 26 | { 27 | return md5.ComputeHash(data); 28 | } 29 | } 30 | 31 | [Benchmark] 32 | public byte[] PackSlow() 33 | { 34 | using (var sha512 = SHA512.Create()) 35 | { 36 | return sha512.ComputeHash(data); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/ArchBenchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5390E273-EF31-4850-AEA6-6D8A546395FE} 8 | Exe 9 | Properties 10 | ArchBenchmark 11 | ArchBenchmark 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | ..\packages\BenchmarkDotNet.0.10.9\lib\net46\BenchmarkDotNet.dll 38 | 39 | 40 | ..\packages\BenchmarkDotNet.Core.0.10.9\lib\net46\BenchmarkDotNet.Core.dll 41 | 42 | 43 | ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.10.9\lib\net46\BenchmarkDotNet.Toolchains.Roslyn.dll 44 | 45 | 46 | ..\packages\InfluxDB.Collector.1.0.0\lib\net45\InfluxDB.Collector.dll 47 | 48 | 49 | ..\packages\InfluxDB.LineProtocol.1.0.0\lib\net45\InfluxDB.LineProtocol.dll 50 | 51 | 52 | ..\packages\Microsoft.CodeAnalysis.Common.2.0.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll 53 | 54 | 55 | ..\packages\Microsoft.CodeAnalysis.CSharp.2.0.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll 56 | 57 | 58 | ..\packages\Microsoft.DotNet.InternalAbstractions.1.0.0\lib\net451\Microsoft.DotNet.InternalAbstractions.dll 59 | 60 | 61 | ..\packages\Microsoft.DotNet.PlatformAbstractions.1.1.1\lib\net451\Microsoft.DotNet.PlatformAbstractions.dll 62 | 63 | 64 | ..\packages\Microsoft.Win32.Registry.4.3.0\lib\net46\Microsoft.Win32.Registry.dll 65 | 66 | 67 | 68 | ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll 69 | 70 | 71 | ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll 72 | True 73 | 74 | 75 | 76 | ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll 77 | 78 | 79 | 80 | ..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll 81 | 82 | 83 | ..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll 84 | 85 | 86 | ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll 87 | True 88 | 89 | 90 | ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll 91 | 92 | 93 | ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll 94 | 95 | 96 | 97 | ..\packages\System.Reflection.Metadata.1.4.2\lib\portable-net45+win8\System.Reflection.Metadata.dll 98 | 99 | 100 | ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll 101 | 102 | 103 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll 104 | 105 | 106 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll 107 | 108 | 109 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll 110 | 111 | 112 | ..\packages\System.Text.Encoding.CodePages.4.3.0\lib\net46\System.Text.Encoding.CodePages.dll 113 | 114 | 115 | ..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll 116 | 117 | 118 | ..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll 119 | 120 | 121 | ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll 131 | 132 | 133 | ..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll 134 | 135 | 136 | ..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll 137 | 138 | 139 | ..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/InfluxExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using BenchmarkDotNet.Exporters; 6 | using BenchmarkDotNet.Loggers; 7 | using BenchmarkDotNet.Reports; 8 | using InfluxDB.Collector; 9 | 10 | namespace ArchBenchmark 11 | { 12 | public class InfluxExporter : IExporter 13 | { 14 | public string Name => nameof(InfluxExporter); 15 | 16 | private readonly Uri influxServer; 17 | private readonly string measurement; 18 | 19 | public InfluxExporter(Uri influxServer, string measurement) 20 | { 21 | this.influxServer = influxServer; 22 | this.measurement = measurement; 23 | } 24 | 25 | public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) 26 | { 27 | using (var writer = CreateInfluxWriter(influxServer)) 28 | { 29 | foreach (var report in summary.Reports) 30 | { 31 | var tags = new Dictionary 32 | { 33 | { "class", report.Benchmark.Target.Type.Name }, 34 | { "method", report.Benchmark.Target.MethodDisplayInfo } 35 | }; 36 | 37 | var nanoseconds = report.ResultStatistics.Mean; 38 | 39 | writer.Measure(measurement, nanoseconds, tags); 40 | } 41 | } 42 | 43 | return Enumerable.Empty(); 44 | } 45 | 46 | public void ExportToLog(Summary summary, ILogger logger) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | 51 | public static MetricsCollector CreateInfluxWriter(Uri influxServer) 52 | { 53 | return new CollectorConfiguration() 54 | .Tag.With("host", Environment.MachineName.ToLower(CultureInfo.InvariantCulture)) 55 | .Batch.AtInterval(TimeSpan.FromSeconds(1)) 56 | .WriteTo.InfluxDB(influxServer, "benchmarks") 57 | .CreateCollector(); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/MyConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Attributes.Exporters; 3 | using BenchmarkDotNet.Configs; 4 | using BenchmarkDotNet.Engines; 5 | using BenchmarkDotNet.Jobs; 6 | 7 | namespace ArchBenchmark 8 | { 9 | public class MyConfig : ManualConfig 10 | { 11 | public MyConfig() 12 | { 13 | var job = new Job(nameof(Job.InProcess), InfrastructureMode.InProcess); 14 | job.Run.LaunchCount = 1; 15 | job.Run.WarmupCount = 1; 16 | job.Run.TargetCount = 3; 17 | job.Run.RunStrategy = RunStrategy.ColdStart; 18 | job.Run.UnrollFactor = 1; 19 | 20 | Add(job); 21 | } 22 | } 23 | 24 | public class InfluxExporterAttribute : ExporterConfigBaseAttribute 25 | { 26 | public InfluxExporterAttribute(string influxServer, string measurement) 27 | : base(new InfluxExporter(new Uri(influxServer), measurement)) 28 | { 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace ArchBenchmark 4 | { 5 | public static class Program 6 | { 7 | public static void Main() 8 | { 9 | BenchmarkRunner.Run(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ArchBenchmark")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ArchBenchmark")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5390e273-ef31-4850-aea6-6d8a546395fe")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/README.md: -------------------------------------------------------------------------------- 1 | # BenchmarkDotNet InfluxDB exporter 2 | 3 | ![ArchBenchmark banner](../../Images/ArchBenchmark.png) 4 | 5 | Dependencies: 6 | 7 | - [InfluxDB](https://portal.influxdata.com/downloads) 8 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchBenchmark/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/ArchDataGen.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {80921DC5-9B10-445B-B861-16705E6A7C3B} 8 | Exe 9 | ArchDataGen 10 | ArchDataGen 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\InfluxDB.Collector.1.0.0\lib\net45\InfluxDB.Collector.dll 37 | 38 | 39 | ..\packages\InfluxDB.LineProtocol.1.0.0\lib\net45\InfluxDB.LineProtocol.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {5390e273-ef31-4850-aea6-6d8a546395fe} 62 | ArchBenchmark 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/FakeDataGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using ArchBenchmark; 5 | 6 | namespace ArchDataGen 7 | { 8 | internal sealed class FakeDataGenerator 9 | { 10 | private static readonly IReadOnlyDictionary MoonDays = new Dictionary 11 | { 12 | [01] = 12, 13 | [02] = 11, 14 | [03] = 12, 15 | [04] = 10, 16 | [05] = 10, 17 | [06] = 09, 18 | [07] = 09, 19 | [08] = 08, 20 | [09] = 06, 21 | [10] = 06, 22 | [11] = 04, 23 | [12] = 04 24 | }; 25 | 26 | private readonly ArchBenchmark.ArchBenchmark arch; 27 | 28 | private FakeDataGenerator() 29 | { 30 | arch = new ArchBenchmark.ArchBenchmark(); 31 | } 32 | 33 | public static void Run() 34 | { 35 | new FakeDataGenerator().Generate(); 36 | } 37 | 38 | private void Generate() 39 | { 40 | using (var writer = InfluxExporter.CreateInfluxWriter(new Uri(ArchBenchmark.ArchBenchmark.InfluxServer))) 41 | { 42 | var now = DateTime.UtcNow.Date; 43 | 44 | for (int past = -360; past <= 0 ; past++) 45 | { 46 | var timestamp = now.AddDays(past); 47 | 48 | var tags = new Dictionary 49 | { 50 | { "class", nameof(ArchBenchmark.ArchBenchmark) }, 51 | { "method", nameof(ArchBenchmark.ArchBenchmark.PackFast) } 52 | }; 53 | 54 | var value = new Dictionary { { "value", Measure(timestamp, () => arch.PackFast()) } }; 55 | writer.Write(ArchBenchmark.ArchBenchmark.Measurement, value, tags, timestamp); 56 | 57 | tags["method"] = nameof(ArchBenchmark.ArchBenchmark.PackSlow); 58 | value = new Dictionary { { "value", Measure(timestamp, () => arch.PackSlow()) } }; 59 | writer.Write(ArchBenchmark.ArchBenchmark.Measurement, value, tags, timestamp); 60 | 61 | Console.WriteLine(past); 62 | } 63 | } 64 | } 65 | 66 | private double Measure(DateTime time, Action action) 67 | { 68 | const int moonIsComming = 5; 69 | const int nanosecondsPerMillisecond = 1000 * 1000; 70 | 71 | var moonDay = MoonDays[time.Month]; 72 | var moonPower = Math.Abs(moonDay - time.Day); 73 | var moonTime = moonPower > moonIsComming ? 0 : moonIsComming - moonPower; 74 | 75 | var timer = Stopwatch.StartNew(); 76 | action(); 77 | return (timer.Elapsed.TotalMilliseconds + moonTime) * nanosecondsPerMillisecond; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/Program.cs: -------------------------------------------------------------------------------- 1 | namespace ArchDataGen 2 | { 3 | public static class Program 4 | { 5 | public static void Main() 6 | { 7 | FakeDataGenerator.Run(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ArchDataGen")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ArchDataGen")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("80921dc5-9b10-445b-b861-16705e6a7c3b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/README.md: -------------------------------------------------------------------------------- 1 | # Fake data generator 2 | 3 | ![ArchDataGen banner](../../Images/ArchDataGen.png) 4 | 5 | Dependencies: 6 | 7 | - [InfluxDB](https://portal.influxdata.com/downloads) 8 | -------------------------------------------------------------------------------- /Metrix/Demo/ArchDataGen/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Metrix/Demo/MetrixDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2015 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchDataGen", "ArchDataGen\ArchDataGen.csproj", "{80921DC5-9B10-445B-B861-16705E6A7C3B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchBenchmark", "ArchBenchmark\ArchBenchmark.csproj", "{5390E273-EF31-4850-AEA6-6D8A546395FE}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {80921DC5-9B10-445B-B861-16705E6A7C3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {80921DC5-9B10-445B-B861-16705E6A7C3B}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {80921DC5-9B10-445B-B861-16705E6A7C3B}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {80921DC5-9B10-445B-B861-16705E6A7C3B}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {5390E273-EF31-4850-AEA6-6D8A546395FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {5390E273-EF31-4850-AEA6-6D8A546395FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {5390E273-EF31-4850-AEA6-6D8A546395FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {5390E273-EF31-4850-AEA6-6D8A546395FE}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A6D0F867-25BA-4CB8-9755-6934D6F09BFE} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Metrix/Demo/README.md: -------------------------------------------------------------------------------- 1 | # Demos 2 | 3 | All demos powered by [InfluxDB](https://www.influxdata.com/time-series-platform/influxdb/) and [Grafana](https://grafana.com/). 4 | 5 | - [BenchmarkDotNet InfluxDB exporter](ArchBenchmark) 6 | - [Fake data generator](ArchDataGen) -------------------------------------------------------------------------------- /Metrix/Images/ArchBenchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/Metrix/Images/ArchBenchmark.png -------------------------------------------------------------------------------- /Metrix/Images/ArchDataGen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/Metrix/Images/ArchDataGen.png -------------------------------------------------------------------------------- /Metrix/Images/Carl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/Metrix/Images/Carl.jpg -------------------------------------------------------------------------------- /Metrix/Metrix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/Metrix/Metrix.pdf -------------------------------------------------------------------------------- /Metrix/README.md: -------------------------------------------------------------------------------- 1 | # The Metrix has you... 2 | 3 | Introduction into Time Series and tools for .Net developers, such as InfluxDB and Grafana. 4 | 5 | ![Big data](./Images/Carl.jpg) 6 | 7 | ## Public Talks 8 | 9 | `[2017-12-09]` [.NET Dev Meetup, Belarus, Minsk](https://www.facebook.com/events/1996515497260556/) ([Pdf slides](../../minsk2017/Metrix/Metrix.pdf)) 10 | `[2017-11-23]` [SpbDotNet, Saint Petersburg](https://spbdotnet.timepad.ru/event/610557/) ([Video](https://www.youtube.com/watch?v=pjmRqIgoyFE), [Pdf slides](../../spbdotnet24/Metrix/Metrix.pdf), [SlideShare slides](https://www.slideshare.net/SpbDotNet/the-metrix-has-you)) 11 | `[2017-11-12]` [DotNext, Moscow](https://dotnext-moscow.ru/2017/msk/talks/2wij6mss4oea0mqi2g0ewk/) ([Video](https://www.youtube.com/watch?v=AFB89L8DLpE), [Pdf slides](../../dotnext2017/Metrix/Metrix.pdf)) 12 | 13 | ## Links 14 | 15 | ### Time Series 16 | 17 | - [Gorilla Paper](http://www.vldb.org/pvldb/vol8/p1816-teller.pdf) 18 | - [Akumuli](http://akumuli.org/) 19 | - [Run-length encoding](https://en.wikipedia.org/wiki/Run-length_encoding) 20 | - [Varints](https://developers.google.com/protocol-buffers/docs/encoding#varints), [ZigZag](https://developers.google.com/protocol-buffers/docs/encoding#types) 21 | - [Dynamic time warping](https://en.wikipedia.org/wiki/Dynamic_time_warping) 22 | - [Sketch-based change detection](https://dl.acm.org/citation.cfm?id=948236) 23 | - [Mobile Phone Based Drunk driving detection](https://www.slideshare.net/nagarajc007/mobile-drunk-driver-detection) 24 | 25 | ### Tools 26 | 27 | - [InfluxData site](https://www.influxdata.com/) 28 | - [Grafana site](https://grafana.com/) 29 | - [App Metrics site](https://www.app-metrics.io/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Репозиторий содержит рабочие материалы и примеры кода к моим докладам. 2 | 3 | # Темы 4 | 5 | - [Build as Code](BuildAsCode) — введение в тему построения, тестирования и публикации приложений на подходе с контейнерной изоляцией и полностью описанном в коде на PowerShell или Nuke Build. 6 | - [The Metrix has you...](Metrix) - introduction into Time Series and tools for .Net developers, such as InfluxDB and Grafana. 7 | - [Structured Logging](StructuredLogging) - introduction into structured logging and tools for .Net developers, such as Serilog and Seq. 8 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/ClusterConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PowerGraphNet 4 | { 5 | internal static class ClusterConfig 6 | { 7 | public static readonly Uri GatewayAddress = Localhost(9000); 8 | public static readonly Uri AuthAddress = Localhost(9001); 9 | public static readonly Uri StaticAddress = Localhost(9002); 10 | public static readonly Uri TextAddress = Localhost(9003); 11 | public static readonly Uri ImageAddress = Localhost(9004); 12 | public static readonly Uri AdAddress = Localhost(9005); 13 | 14 | private static Uri Localhost(int port) 15 | { 16 | return new UriBuilder("http", Environment.MachineName, port).Uri; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/AdController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Web.Http; 4 | 5 | namespace PowerGraphNet.Controllers 6 | { 7 | public sealed class AdController : ApiController 8 | { 9 | [HttpGet] 10 | [Route("api/ad")] 11 | public string GetAd() 12 | { 13 | Thread.Sleep(TimeSpan.FromSeconds(1.1)); 14 | 15 | return @" |_____/ \___/ \__|_| \_|\___/_/\_\\__|"; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/AuthController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace PowerGraphNet.Controllers 4 | { 5 | public sealed class AuthController : ApiController 6 | { 7 | [HttpGet] 8 | [Route("api/auth")] 9 | public string GetAuth() 10 | { 11 | return @" | __ \ | | | \ | | | | "; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/ImageController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace PowerGraphNet.Controllers 4 | { 5 | public sealed class ImageController : ApiController 6 | { 7 | [HttpGet] 8 | [Route("api/image")] 9 | public string GetImage() 10 | { 11 | return @" | |__| | (_) | |_| |\ | __/> <| |_ "; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/IndexController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Web.Http; 4 | using PowerGraphNet.Utils; 5 | 6 | namespace PowerGraphNet.Controllers 7 | { 8 | public sealed class IndexController : ApiController 9 | { 10 | [HttpGet] 11 | [Route("index")] 12 | public async Task GetIndex() 13 | { 14 | var requestId = Request.Headers.GetAppRequestId(); 15 | 16 | var auth = Client.GetString(ClusterConfig.AuthAddress, "api/auth", requestId, WebServer.ServiceName); 17 | var stat = Client.GetString(ClusterConfig.StaticAddress, "api/static", requestId, WebServer.ServiceName); 18 | var ad = Client.GetString(ClusterConfig.AdAddress, "api/ad", requestId, WebServer.ServiceName); 19 | 20 | return String.Format( 21 | "{1}{0}{2}{0}{3}{0}{4}{0}", 22 | Environment.NewLine, 23 | " _____ _ _ _ _ ", 24 | await auth, 25 | await stat, 26 | await ad); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/StaticController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Web.Http; 4 | using PowerGraphNet.Utils; 5 | 6 | namespace PowerGraphNet.Controllers 7 | { 8 | public sealed class StaticController : ApiController 9 | { 10 | [HttpGet] 11 | [Route("api/static")] 12 | public async Task GetStatic() 13 | { 14 | var requestId = Request.Headers.GetAppRequestId(); 15 | 16 | var text = Client.GetString(ClusterConfig.TextAddress, "api/text", requestId, WebServer.ServiceName); 17 | var image = Client.GetString(ClusterConfig.ImageAddress, "api/image", requestId, WebServer.ServiceName); 18 | 19 | return String.Format( 20 | "{1}{0}{2}{0}{3}", 21 | Environment.NewLine, 22 | @" | | | | ___ | |_| \| | _____ _| |_ ", 23 | await text, 24 | await image); 25 | 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Controllers/TextController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | 3 | namespace PowerGraphNet.Controllers 4 | { 5 | public sealed class TextController : ApiController 6 | { 7 | [HttpGet] 8 | [Route("api/text")] 9 | public string GetText() 10 | { 11 | return @" | | | |/ _ \| __| . ` |/ _ \ \/ / __|"; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/LoggerMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | using Microsoft.Owin; 5 | using PowerGraphNet.Utils; 6 | using Serilog; 7 | using Serilog.Context; 8 | 9 | namespace PowerGraphNet 10 | { 11 | public sealed class LoggerMiddleware : OwinMiddleware 12 | { 13 | private static readonly ILogger RequestLogger = LogFactory 14 | .CreateLogger() 15 | .ForEventType("HttpRequest"); 16 | 17 | private static readonly ILogger ResponseLogger = LogFactory 18 | .CreateLogger() 19 | .ForEventType("HttpResponse"); 20 | 21 | public LoggerMiddleware(OwinMiddleware next) 22 | : base(next) 23 | { 24 | } 25 | 26 | public override async Task Invoke(IOwinContext context) 27 | { 28 | var request = context.Request; 29 | var requestId = EnsureRequestId(request); 30 | var referer = GetReferer(request); 31 | 32 | var requestLogger = RequestLogger.ForContext("Referer", referer); 33 | 34 | using (LogContext.PushProperty("AppRequestId", requestId)) 35 | { 36 | requestLogger.Information( 37 | "Processing {RequestMethod} {Path}", 38 | request.Method, 39 | request.Path.Value); 40 | 41 | if (request.QueryString.HasValue) 42 | { 43 | requestLogger.Information("Query {RequestQuery}", request.QueryString.Value); 44 | } 45 | 46 | var timer = Stopwatch.StartNew(); 47 | 48 | await Next.Invoke(context); 49 | 50 | timer.Stop(); 51 | ResponseLogger.Information( 52 | "Responce {ResponseStatus} {ResponseStatusName} ({RequestDuration})", 53 | context.Response.StatusCode, 54 | context.Response.ReasonPhrase, 55 | timer.Elapsed); 56 | } 57 | } 58 | 59 | private static string EnsureRequestId(IOwinRequest request) 60 | { 61 | string appRequestId = request.Headers["AppRequestId"]; 62 | 63 | if (appRequestId == null) 64 | { 65 | appRequestId = Guid.NewGuid().ToString("N"); 66 | request.Headers["AppRequestId"] = appRequestId; 67 | } 68 | 69 | return appRequestId; 70 | } 71 | 72 | private static string GetReferer(IOwinRequest request) 73 | { 74 | return request.Headers["Referer"]; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/PowerGraphNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EB418329-F14A-4735-A69C-10A4174DB79A} 8 | Exe 9 | Properties 10 | PowerGraphNet 11 | PowerGraphNet 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Microsoft.Owin.2.1.0\lib\net45\Microsoft.Owin.dll 37 | True 38 | 39 | 40 | ..\packages\Microsoft.Owin.Host.HttpListener.2.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll 41 | True 42 | 43 | 44 | ..\packages\Microsoft.Owin.Hosting.2.1.0\lib\net45\Microsoft.Owin.Hosting.dll 45 | True 46 | 47 | 48 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 49 | True 50 | 51 | 52 | ..\packages\Owin.1.0\lib\net40\Owin.dll 53 | True 54 | 55 | 56 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.dll 57 | True 58 | 59 | 60 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll 61 | True 62 | 63 | 64 | ..\packages\Serilog.Sinks.Literate.1.2.0\lib\net45\Serilog.Sinks.Literate.dll 65 | True 66 | 67 | 68 | 69 | 70 | 71 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 72 | True 73 | 74 | 75 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 76 | True 77 | 78 | 79 | ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll 80 | True 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 121 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PowerGraphNet")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PowerGraphNet")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a9e37440-b181-46df-b287-f812131e3d1e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Proto/Request.json: -------------------------------------------------------------------------------- 1 | { 2 | "Timestamp": "2015-11-29T17:48:39.5808170+03:00", 3 | "Level": "Information", 4 | "MessageTemplate": "Processing {RequestMethod} {Path}", 5 | "RenderedMessage": "Processing GET /api/auth", 6 | "Properties": { 7 | "RequestMethod": "GET", 8 | "Path": "/api/auth", 9 | "Referer": "Gateway", 10 | "EventType": "HttpRequest", 11 | "Host": "Auth", 12 | "AppRequestId": "a88a3c1a6d394c9abc68db1d70cb3ac4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Proto/Response.json: -------------------------------------------------------------------------------- 1 | { 2 | "Timestamp": "2015-11-29T17:48:39.8128170+03:00", 3 | "Level": "Information", 4 | "MessageTemplate": "Responce {ResponseStatus} {ResponseStatusName} ({RequestDuration})", 5 | "RenderedMessage": "Responce 200 OK (00:00:00.2299350)", 6 | "Properties": { 7 | "ResponseStatus": 200, 8 | "ResponseStatusName": "OK", 9 | "RequestDuration": "00:00:00.2299350", 10 | "EventType": "HttpResponse", 11 | "Host": "Auth", 12 | "AppRequestId": "a88a3c1a6d394c9abc68db1d70cb3ac4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/README.md: -------------------------------------------------------------------------------- 1 | # Distributed request analysis 2 | 3 | ![PowerGraphNet banner](../../Images/PowerGraphNet.png) 4 | 5 | Dependencies: 6 | 7 | - PowerShell 8 | - [GraphViz](http://www.graphviz.org) -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Scripts/Receive-Logs.ps1: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | $rootPath = Split-Path -Parent $MyInvocation.MyCommand.Path 4 | $server = Join-Path $rootPath "..\bin\Debug\PowerGraphNet.exe" 5 | $logIn = Join-Path $rootPath "..\bin\Debug\Logs" 6 | $logOut = Join-Path $rootPath "http-all.log" 7 | 8 | if (Test-Path $logIn) { del $logIn -Recurse } 9 | 10 | ("Gateway", "Auth", "Static", "Text", "Image", "Ad") | % { start "$server" $_ } 11 | 12 | clear 13 | Invoke-RestMethod http://localhost:9000/index?slug 14 | 15 | kill -ProcessName PowerGraphNet 16 | 17 | cat "$logIn\*.log" > $logOut -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Scripts/RegisterPorts.cmd: -------------------------------------------------------------------------------- 1 | :: Run as administrator 2 | netsh http add urlacl url=http://+:9000/ sddl=D:(A;;GX;;;WD) 3 | netsh http add urlacl url=http://+:9001/ sddl=D:(A;;GX;;;WD) 4 | netsh http add urlacl url=http://+:9002/ sddl=D:(A;;GX;;;WD) 5 | netsh http add urlacl url=http://+:9003/ sddl=D:(A;;GX;;;WD) 6 | netsh http add urlacl url=http://+:9004/ sddl=D:(A;;GX;;;WD) 7 | netsh http add urlacl url=http://+:9005/ sddl=D:(A;;GX;;;WD) 8 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Scripts/Show-Requests.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | 3 | clear 4 | 5 | $rootPath = Split-Path -Parent $MyInvocation.MyCommand.Path 6 | $logPath = Join-Path $rootPath "http-all.log" 7 | $dotPath = Join-Path $rootPath "GraphViz\bin\dot.exe" 8 | $gvPath = [IO.Path]::ChangeExtension($logPath, "gv") 9 | $imgPath = [IO.Path]::ChangeExtension($logPath, "png") 10 | 11 | 12 | ############## 13 | ### Checks ### 14 | 15 | if (-not (Test-Path $dotPath)) 16 | { 17 | Write-Error "Please, install GraphViz from http://www.graphviz.org" 18 | return 19 | } 20 | 21 | if (-not (Test-Path $logPath)) 22 | { 23 | Write-Error "Please, run Receive-Logs.ps1 first" 24 | return 25 | } 26 | 27 | ################ 28 | ### Read log ### 29 | 30 | $json = @("[") 31 | $json += cat $logPath 32 | $json += "{}]" # suppress last comma 33 | 34 | $log = $json | ConvertFrom-Json 35 | $log = $log[0..($log.Length-2)] # remove last empty record 36 | 37 | $requestId = $log | 38 | select -ExpandProperty Properties | 39 | where { $_.RequestQuery -eq 'slug' } | 40 | select -ExpandProperty AppRequestId -First 1 41 | 42 | $log = $log | where { $_.Properties.AppRequestId -eq $requestId } 43 | 44 | $log | foreach { $_.RenderedMessage } 45 | 46 | 47 | ########################## 48 | ### Get slow responses ### 49 | 50 | $durations = @{} 51 | $log | 52 | select -ExpandProperty Properties | 53 | where { $_.EventType -eq 'HttpResponse' } | 54 | foreach { $durations.Add($_.Host, ([TimeSpan]$_.RequestDuration).TotalMilliseconds) } 55 | 56 | $slowHosts = $durations.GetEnumerator() | 57 | where { $_.Value -gt 1000 } | 58 | select -ExpandProperty Name 59 | 60 | 61 | ######################### 62 | ### Get request flows ### 63 | $flows = $log | 64 | select -ExpandProperty Properties | 65 | where { $_.EventType -eq 'HttpRequest' } | 66 | select @{ Name="From"; Expression={$_.Referer} }, @{ N="To"; E={$_.Host} }, @{ N="Duration"; E={$durations[$_.Host]} } 67 | 68 | 69 | ################### 70 | ### Build graph ### 71 | $graph = 72 | 'digraph Requests {', 73 | ' rankdir="LR";', 74 | ' node [shape="box", style="filled", fillcolor="green", fontcolor="black"];', 75 | '' 76 | 77 | $graph += $slowHosts | 78 | foreach { ' "{0}" [fillcolor="red"];' -f $_ } 79 | $graph += '' 80 | 81 | $graph += $flows | 82 | where { $_.From -ne $null } | # skip root 83 | foreach { ' "{0}" -> "{1}" [label="{2}ms"];' -f $_.From,$_.To,[int]$_.Duration } 84 | $graph += '}' 85 | 86 | $graph | Set-Content $gvPath 87 | 88 | &$dotPath -Gdpi=256 -Tpng $gvPath "-o$imgPath" 89 | &$imgPath 90 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Utils/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using Newtonsoft.Json; 5 | 6 | namespace PowerGraphNet.Utils 7 | { 8 | internal static class Client 9 | { 10 | private static readonly HttpClient ServiceClient = new HttpClient(); 11 | 12 | public static async Task GetString(Uri serviceAddress, string path, string appRequestId, string referer) 13 | { 14 | var service = new UriBuilder(serviceAddress) { Path = path }; 15 | var request = new HttpRequestMessage(HttpMethod.Get, service.Uri); 16 | request.Headers.Add("Referer", referer); 17 | request.Headers.Add("AppRequestId", appRequestId); 18 | 19 | var response = await ServiceClient.SendAsync(request); 20 | var content = await response.Content.ReadAsStringAsync(); 21 | 22 | return JsonConvert.DeserializeObject(content); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Utils/HttpRequestHeadersExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Http.Headers; 4 | 5 | namespace PowerGraphNet.Utils 6 | { 7 | public static class HttpRequestHeadersExtensions 8 | { 9 | public static string GetAppRequestId(this HttpRequestHeaders header) 10 | { 11 | if (header == null) throw new ArgumentNullException("header"); 12 | 13 | var appRequestIds = header.GetValues("AppRequestId"); 14 | var appRequestId = appRequestIds.FirstOrDefault(); 15 | 16 | if (appRequestId == null) 17 | { 18 | throw new ApplicationException("AppRequestId not found"); 19 | } 20 | 21 | return appRequestId; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/Utils/LogFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Serilog; 4 | using Serilog.Formatting.Json; 5 | using Serilog.Sinks.IOFile; 6 | 7 | namespace PowerGraphNet.Utils 8 | { 9 | internal static class LogFactory 10 | { 11 | private static ILogger BaseLogger; 12 | 13 | public static void Initialize(string serviceName) 14 | { 15 | var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs", serviceName + ".log"); 16 | 17 | var configuration = new LoggerConfiguration() 18 | .Enrich.WithProperty("Host", serviceName) 19 | .Enrich.FromLogContext() 20 | .MinimumLevel.Verbose() 21 | .WriteTo.LiterateConsole( 22 | outputTemplate: 23 | "[{Timestamp:HH:mm:ss.FFFF}] {Message}{NewLine}{Exception}") 24 | .WriteTo.Sink(new FileSink(filePath, new JsonFormatter(closingDelimiter: "," + Environment.NewLine, renderMessage: true), null)); 25 | 26 | BaseLogger = configuration.CreateLogger(); 27 | } 28 | 29 | public static ILogger CreateLogger() 30 | { 31 | return BaseLogger; 32 | } 33 | 34 | public static ILogger ForEventType(this ILogger baseLogger, string eventType) 35 | { 36 | return baseLogger.ForContext("EventType", eventType); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/WebServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Web.Http; 4 | using Microsoft.Owin.Hosting; 5 | using Owin; 6 | using PowerGraphNet.Utils; 7 | using Serilog; 8 | 9 | namespace PowerGraphNet 10 | { 11 | public sealed class WebServer 12 | { 13 | private static ILogger Logger; 14 | public static string ServiceName; 15 | 16 | public static void Main(string[] args) 17 | { 18 | if (args.Length == 0) 19 | { 20 | throw new ArgumentException("Enter service name"); 21 | } 22 | 23 | ServiceName = args[0]; 24 | Console.Title = ServiceName; 25 | 26 | using (Start(GetAddressFor(ServiceName))) 27 | { 28 | Console.ReadLine(); 29 | } 30 | 31 | Logger.Information("Web server {ServiceName} shutdown", ServiceName); 32 | } 33 | 34 | private static IDisposable Start(Uri remoteAddress) 35 | { 36 | LogFactory.Initialize(ServiceName); 37 | Logger = LogFactory.CreateLogger(); 38 | 39 | var listenAddress = ConvertToListenAddress(remoteAddress); 40 | var listener = WebApp.Start(listenAddress, InitializeApp); 41 | 42 | Logger.Information("Web server {ServiceName} started at {ServicePort}", ServiceName, remoteAddress.Port); 43 | 44 | return listener; 45 | } 46 | 47 | private static void InitializeApp(IAppBuilder app) 48 | { 49 | var config = new HttpConfiguration(); 50 | 51 | config.MapHttpAttributeRoutes(); 52 | 53 | app.Use(); 54 | app.UseWebApi(config); 55 | } 56 | 57 | private static string ConvertToListenAddress(Uri remoteAddress) 58 | { 59 | return String.Format(CultureInfo.InvariantCulture, "{0}://+:{1}/", remoteAddress.Scheme, remoteAddress.Port); 60 | } 61 | 62 | private static Uri GetAddressFor(string serviceName) 63 | { 64 | switch (serviceName) 65 | { 66 | case "Gateway": 67 | return ClusterConfig.GatewayAddress; 68 | case "Auth": 69 | return ClusterConfig.AuthAddress; 70 | case "Static": 71 | return ClusterConfig.StaticAddress; 72 | case "Text": 73 | return ClusterConfig.TextAddress; 74 | case "Image": 75 | return ClusterConfig.ImageAddress; 76 | case "Ad": 77 | return ClusterConfig.AdAddress; 78 | } 79 | 80 | throw new ArgumentOutOfRangeException(serviceName); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/PowerGraphNet/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/README.md: -------------------------------------------------------------------------------- 1 | # Demos 2 | 3 | All demos powered by [Serilog](http://serilog.net/) 4 | 5 | - [Distributed request analysis (PowerShell)](PowerGraphNet) 6 | - [Fraud detector (T-SQL)](SqlFraud) 7 | - [Seq data generator](SeqEditor) -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/Document.cs: -------------------------------------------------------------------------------- 1 | namespace SeqEditor 2 | { 3 | internal sealed class Document 4 | { 5 | public readonly string Name; 6 | public int SaveCount; 7 | 8 | public Document(string name) 9 | { 10 | Name = name; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/DocumentRepository.cs: -------------------------------------------------------------------------------- 1 | namespace SeqEditor 2 | { 3 | internal sealed class DocumentRepository 4 | { 5 | private readonly Document documentStore; 6 | 7 | public DocumentRepository(string documentName) 8 | { 9 | documentStore = new Document(documentName); 10 | } 11 | 12 | public Document FindDocument() 13 | { 14 | return documentStore; 15 | } 16 | 17 | public void Save(Document document) 18 | { 19 | document.SaveCount++; 20 | 21 | if (document.SaveCount % 2 == 0) 22 | { 23 | throw new OptimisticConcurrencyException(); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/OptimisticConcurrencyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SeqEditor 4 | { 5 | internal class OptimisticConcurrencyException : Exception 6 | { 7 | public OptimisticConcurrencyException() 8 | : base("The document you attempted to edit was modified by another user after you got the original version") 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/Program.cs: -------------------------------------------------------------------------------- 1 | using Serilog; 2 | 3 | namespace SeqEditor 4 | { 5 | public sealed class Program 6 | { 7 | public static readonly ILogger BaseLogger = InitializeLogger(); 8 | 9 | public static void Main(string[] args) 10 | { 11 | var story = new Story(); 12 | story.Run(); 13 | } 14 | 15 | private static ILogger InitializeLogger() 16 | { 17 | 18 | var configuration = new LoggerConfiguration() 19 | //.Enrich.FromLogContext() 20 | .Enrich.WithThreadId() 21 | .MinimumLevel.Verbose() 22 | .WriteTo.Seq("http://localhost:5342") 23 | .WriteTo.LiterateConsole( 24 | outputTemplate: "[{Timestamp:HH:mm:ss.FFFF}] {Message}{NewLine}{Exception}"); 25 | 26 | return configuration.CreateLogger(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SeqEditor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SeqEditor")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("19ffbde2-eb71-4c0f-b8cf-a97fe96c75f6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/README.md: -------------------------------------------------------------------------------- 1 | # Seq data generator 2 | 3 | ![SeqEditor banner](../../Images/SeqEditor.png) 4 | 5 | Dependencies: 6 | 7 | - [Seq](http://getseq.net/) -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/SeqEditor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0C8E7448-22EF-423C-88D9-8827CD945B96} 8 | Exe 9 | Properties 10 | SeqEditor 11 | SeqEditor 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.dll 37 | True 38 | 39 | 40 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll 41 | True 42 | 43 | 44 | ..\packages\Serilog.Sinks.Literate.1.2.0\lib\net45\Serilog.Sinks.Literate.dll 45 | True 46 | 47 | 48 | ..\packages\Serilog.Sinks.Seq.1.5.27\lib\net45\Serilog.Sinks.Seq.dll 49 | True 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 80 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/Session.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Serilog; 4 | 5 | namespace SeqEditor 6 | { 7 | internal sealed class Session 8 | { 9 | public readonly string UserName; 10 | public readonly DocumentRepository Repository; 11 | 12 | private readonly ILogger logger; 13 | private readonly string[] allChanges = Enum.GetNames(typeof(ChangeType)); 14 | private static readonly Random Random = new Random(); 15 | 16 | private Document document; 17 | 18 | public Session(string userName, DocumentRepository repository) 19 | { 20 | UserName = userName; 21 | Repository = repository; 22 | 23 | logger = Program.BaseLogger.ForContext("SessionId", UserName); 24 | } 25 | 26 | public void Login() 27 | { 28 | logger.Information("User {UserName} logged in", UserName); 29 | } 30 | 31 | public void OpenDocument() 32 | { 33 | logger.Information("Searching document..."); 34 | document = Repository.FindDocument(); 35 | 36 | logger.Information("Found document {Document}", document.Name); 37 | logger.Information("Open document {Document}", document.Name); 38 | } 39 | 40 | public void EditDocument() 41 | { 42 | var changeType = allChanges[Random.Next(0, allChanges.Length - 1)]; 43 | 44 | var duration = TimeSpan.FromMilliseconds(Random.Next(100, 500)); 45 | Thread.Sleep(duration); 46 | 47 | logger.Information("{Change} in document {Document} (take {Duration})", changeType, document.Name, duration); 48 | } 49 | 50 | public void SaveDocument() 51 | { 52 | logger.Information("Saving document {Document}...", document.Name); 53 | 54 | try 55 | { 56 | Repository.Save(document); 57 | } 58 | catch (Exception exc) 59 | { 60 | logger.Error(exc, "Save document {Document} failed", document.Name); 61 | return; 62 | } 63 | 64 | logger.Information("Document {Document} saved", document.Name); 65 | } 66 | 67 | public void HackTheWorld() 68 | { 69 | logger.Warning("Hack the world"); 70 | } 71 | 72 | public void MakeError() 73 | { 74 | logger.Error("Buffer overflow"); 75 | } 76 | 77 | private enum ChangeType 78 | { 79 | AddTitle, 80 | RemoveTitle, 81 | ChangeTitle, 82 | AddText, 83 | RemoveText, 84 | ChangeText, 85 | AddPicture, 86 | RemovePicture, 87 | ResizePicture, 88 | ChangeStyle, 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/Story.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace SeqEditor 6 | { 7 | internal sealed class Story 8 | { 9 | public void Run() 10 | { 11 | var mainRepository = new DocumentRepository("The Outer Limits"); 12 | var user1 = new Session("Acid Burn", mainRepository); 13 | var user2 = new Session("Zero Cool", mainRepository); 14 | 15 | var secondRepository = new DocumentRepository("Neuromancer"); 16 | var friends = new[] 17 | { 18 | user1, 19 | user2, 20 | new Session("Phantom Phreak", secondRepository), 21 | new Session("Lord Nikon", secondRepository), 22 | new Session("Mr. The Plague", secondRepository), 23 | new Session("Cereal Killer", secondRepository), 24 | new Session("Crash Override", secondRepository) 25 | }; 26 | 27 | //LongEdit(friends); 28 | 29 | ShortEdit(friends); 30 | 31 | user1.SaveDocument(); 32 | user2.SaveDocument(); 33 | } 34 | 35 | private static void LongEdit(Session[] friends) 36 | { 37 | var random = new Random(); 38 | 39 | foreach (var friend in friends) 40 | { 41 | friend.Login(); 42 | friend.OpenDocument(); 43 | } 44 | 45 | while (true) 46 | { 47 | Parallel.ForEach(friends, friend => 48 | { 49 | var wait = TimeSpan.FromSeconds(random.Next(0, 60)); 50 | Thread.Sleep(wait); 51 | 52 | switch (random.Next(0, 100)) 53 | { 54 | case 0: 55 | friend.MakeError(); 56 | break; 57 | case 1: 58 | friend.HackTheWorld(); 59 | break; 60 | default: 61 | friend.EditDocument(); 62 | break; 63 | } 64 | }); 65 | } 66 | } 67 | 68 | private static void ShortEdit(Session[] friends) 69 | { 70 | Parallel.ForEach(friends, friend => 71 | { 72 | friend.Login(); 73 | friend.OpenDocument(); 74 | 75 | for (int i = 0; i < 50; i++) 76 | { 77 | friend.EditDocument(); 78 | } 79 | }); 80 | } 81 | 82 | private static void OldLogger() 83 | { 84 | var logger = Program.BaseLogger.ForContext("Logger", "Old"); 85 | 86 | logger.Information( 87 | "{0} is the {1} prime number. Its mirror, {2}, is the {3} and its mirror, {4}, is the product of multiplying {5}", 88 | 73, 89 | "21st", 90 | 37, 91 | "12th", 92 | 21, 93 | "7 and 3" 94 | ); 95 | 96 | const string userName = "People"; 97 | logger.Warning("Hey {0}, stop using old logger!", userName); 98 | 99 | } 100 | 101 | } 102 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SeqEditor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SqlFraud 4 | { 5 | public sealed class Order 6 | { 7 | public int Id { get; set; } 8 | public int UserId { get; set; } 9 | public DateTime Date { get; set; } 10 | public string Product { get; set; } 11 | public int Quantity { get; set; } 12 | public decimal Price { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Net; 7 | using Dapper; 8 | using Serilog; 9 | using Serilog.Context; 10 | 11 | namespace SqlFraud 12 | { 13 | public sealed class Program 14 | { 15 | private const string ConnectionString = "Server=(local);Database=DotNext;Trusted_Connection=True;"; 16 | #pragma warning disable 618 17 | private static readonly long MaxAddress = IPAddress.Parse("255.255.255.255").Address; 18 | #pragma warning restore 618 19 | 20 | private readonly Random random = new Random(); 21 | private readonly ILogger baseLogger; 22 | private readonly ILogger purchaseLogger; 23 | 24 | private Program() 25 | { 26 | baseLogger = InitializeLogger(); 27 | purchaseLogger = baseLogger.ForContext("EventType", "PurchaseCommited"); 28 | } 29 | 30 | public static void Main() 31 | { 32 | Console.Title = "Fraud Generator"; 33 | 34 | var program = new Program(); 35 | program.GenerateLogs(); 36 | } 37 | 38 | private void GenerateLogs() 39 | { 40 | var orders = LoadOrders(); 41 | baseLogger.Debug("Start generating logs for {OrderCount} orders", orders.Count); 42 | 43 | var users = orders.GroupBy(o => o.UserId); 44 | foreach (var user in users) 45 | { 46 | var userId = user.Key; 47 | var userOrders = user.ToList(); 48 | var fraudCount = random.Next(1, 4); 49 | 50 | var homeAddress = NewAddress(); 51 | GenerateUserLog(userId, homeAddress, userOrders.Skip(fraudCount)); 52 | 53 | var guestAddress = NewAddress(); 54 | GenerateUserLog(userId, guestAddress, userOrders.Take(fraudCount)); 55 | } 56 | 57 | baseLogger.Debug("Logs generated"); 58 | } 59 | 60 | private void GenerateUserLog(int userId, IPAddress userAddress, IEnumerable orders) 61 | { 62 | using (LogContext.PushProperty("UserAddress", userAddress)) 63 | { 64 | foreach (var order in orders) 65 | { 66 | purchaseLogger.Information( 67 | "User {UserId} made a purchase {Product} x{Quantity} (#{OrderId})", 68 | userId, 69 | order.Product, 70 | order.Quantity, 71 | order.Id); 72 | } 73 | } 74 | } 75 | 76 | private static ILogger InitializeLogger() 77 | { 78 | var configuration = new LoggerConfiguration() 79 | .Enrich.FromLogContext() 80 | .MinimumLevel.Verbose() 81 | .WriteTo.LiterateConsole( 82 | outputTemplate: "[{Timestamp:HH:mm:ss.FFFF}] {Message}{NewLine}{Exception}") 83 | .WriteTo.MSSqlServer(ConnectionString, "Logs", 84 | additionalDataColumns: new[] { new DataColumn { ColumnName = "EventType", DataType = typeof(string) } }); 85 | 86 | return configuration.CreateLogger(); 87 | } 88 | 89 | private static IReadOnlyList LoadOrders() 90 | { 91 | using (var connection = new SqlConnection(ConnectionString)) 92 | { 93 | connection.Open(); 94 | 95 | var orders = connection.Query("select * from Orders").ToList(); 96 | return orders; 97 | } 98 | } 99 | 100 | private IPAddress NewAddress() 101 | { 102 | var buff = new byte[8]; 103 | random.NextBytes(buff); 104 | long longRand = BitConverter.ToInt64(buff, 0); 105 | 106 | return new IPAddress(Math.Abs(longRand % MaxAddress)); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SqlFraud")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SqlFraud")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a64f583f-ec97-4443-aa3f-896f0ce440dc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/README.md: -------------------------------------------------------------------------------- 1 | # Fraud detector 2 | 3 | ![SqlFraud banner](../../Images/SqlFraud.png) 4 | 5 | Dependencies: 6 | 7 | - MSSQL Server -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/Scripts/Fraud.sql: -------------------------------------------------------------------------------- 1 | select 2 | [UserName] = u.Name, 3 | [ZoneName] = z.Name, 4 | [PurchaseCount] = count(*), 5 | [PurchasePercent] = cast(cast(count(*) / t.Count * 100 as int) as varchar(255)) + '%' 6 | from 7 | ( 8 | select 9 | [OrderId] = l.Properties.value('(/properties/property[@key="OrderId"])[1]', 'varchar(255)'), 10 | [NumberAddress] = dbo.IPToInt(l.Properties.value('(/properties/property[@key="UserAddress"])[1]', 'varchar(255)')) 11 | from Logs l 12 | where l.EventType = '"PurchaseCommited"' 13 | ) p 14 | join Orders o on o.Id = p.OrderId 15 | join Users u on u.Id = o.UserId 16 | cross apply (select [Count] = cast(count(*) as decimal(19, 4)) from Orders ot where ot.UserId = u.Id) t 17 | join GeoZones z on z.MinIPNumber <= p.NumberAddress and z.MaxIPNumber >= p.NumberAddress 18 | group by u.Name, t.Count, z.Name 19 | order by u.Name, count(*) desc -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/Scripts/Prepare.sql: -------------------------------------------------------------------------------- 1 | /* 2 | drop table GeoZones 3 | drop table Orders 4 | drop table Users 5 | drop table Logs 6 | */ 7 | 8 | if object_id('Logs', 'U') is null 9 | begin 10 | create table Logs 11 | ( 12 | [Id] int not null identity(1, 1) 13 | constraint PK_Logs primary key clustered, 14 | [Message] varchar(max) null, 15 | [MessageTemplate] nvarchar(max) null, 16 | [Level] varchar(128) null, 17 | [TimeStamp] datetime not null, 18 | [Exception] varchar(max) null, 19 | [Properties] xml null, 20 | [EventType] varchar(255) null 21 | ) 22 | end 23 | go 24 | 25 | if object_id('Users', 'U') is null 26 | begin 27 | create table Users 28 | ( 29 | [Id] int not null identity(1, 1) 30 | constraint PK_Users primary key clustered, 31 | [Name] varchar(255) not null 32 | ) 33 | end 34 | go 35 | 36 | if not exists (select * from Users) 37 | begin 38 | insert into Users 39 | ([Name]) 40 | values 41 | ('Acid Burn'), 42 | ('Phantom Phreak'), 43 | ('Lord Nikon'), 44 | ('Mr. The Plague'), 45 | ('Cereal Killer'), 46 | ('Zero Cool'), 47 | ('Crash Override') 48 | end 49 | go 50 | 51 | 52 | if object_id('Orders', 'U') is null 53 | begin 54 | create table Orders 55 | ( 56 | [Id] int not null identity(1, 1) 57 | constraint PK_Orders primary key clustered, 58 | [UserId] int not null 59 | constraint FK_Orders_Users foreign key references Users(Id), 60 | [Date] datetime not null, 61 | [Product] varchar(255) not null, 62 | [Quantity] int not null, 63 | [Price] decimal(19, 4) not null 64 | ) 65 | end 66 | go 67 | 68 | if not exists (select * from Orders) 69 | begin 70 | insert into Orders 71 | ([UserId], [Date], [Product], [Quantity], [Price]) 72 | select 73 | u.Id, 74 | getdate(), 75 | f.Name, 76 | 1 + abs(checksum(newId())) % 100, 77 | 1000 + cast(abs(checksum(newId())) % 1000000 as decimal(19, 4)) /100 78 | from Users u 79 | cross join 80 | ( 81 | select [Name] = 'Apricot' union all 82 | select 'Avocado' union all 83 | select 'Banana' union all 84 | select 'Bilberry' union all 85 | select 'Blackberry' union all 86 | select 'Blackcurrant' union all 87 | select 'Blueberry' union all 88 | select 'Currant' union all 89 | select 'Cherry' union all 90 | select 'Cloudberry' union all 91 | select 'Coconut' union all 92 | select 'Cranberry' union all 93 | select 'Damson' union all 94 | select 'Gooseberry' union all 95 | select 'Grape' union all 96 | select 'Huckleberry' union all 97 | select 'Lemon' union all 98 | select 'Lime' union all 99 | select 'Melon' union all 100 | select 'Orange' union all 101 | select 'Pumpkin' union all 102 | select 'Raspberry' union all 103 | select 'Squash' union all 104 | select 'Tomato' 105 | ) f 106 | end 107 | go 108 | 109 | if object_id('GeoZones', 'U') is null 110 | begin 111 | create table GeoZones 112 | ( 113 | [Id] int not null identity(1, 1) 114 | constraint PK_GeoZones primary key clustered, 115 | [Name] varchar(255) not null, 116 | [MinIPNumber] bigint not null, 117 | [MaxIPNumber] bigint not null 118 | ) 119 | end 120 | go 121 | 122 | if not exists (select * from GeoZones) 123 | begin 124 | declare @max bigint = dbo.IPToInt('255.255.255.255') 125 | declare @size bigint = @max / 10 126 | 127 | insert into GeoZones 128 | ([Name], [MinIPNumber], [MaxIPNumber]) 129 | select 130 | c.Name, 131 | cast(@size * c.[Rank] as bigint), 132 | cast(@size * (c.[Rank] + 1) as bigint) + (case c.[Rank] when 9 then 0 else -1 end) 133 | from 134 | ( 135 | -- Top Ten Hacking Countries from Bloomberg 136 | select [Rank] = 0, [Name] = 'China' union all 137 | select 1, 'United States' union all 138 | select 2, 'Turkey' union all 139 | select 3, 'Russia' union all 140 | select 4, 'Taiwan' union all 141 | select 5, 'Brazil' union all 142 | select 6, 'Romania' union all 143 | select 7, 'India' union all 144 | select 8, 'Italy' union all 145 | select 9, 'Hungary' 146 | ) c 147 | end 148 | go 149 | 150 | if object_id('IntToIP', 'FN') is not null 151 | drop function dbo.IntToIP 152 | go 153 | 154 | create function dbo.IntToIP(@number bigint) 155 | returns varchar(255) 156 | begin 157 | 158 | return 159 | convert(varchar(3), (@number/16777216) & 255) + '.' + 160 | convert(varchar(3), (@number/65536) & 255) + '.' + 161 | convert(varchar(3), (@number/256) & 255) + '.' + 162 | convert(varchar(3), @number & 255) 163 | end 164 | go 165 | 166 | if object_id('IPToInt', 'FN') is not null 167 | drop function dbo.IPToInt 168 | go 169 | 170 | create function dbo.IPToInt(@address varchar(255)) 171 | returns bigint 172 | begin 173 | 174 | return 175 | convert(bigint, parseName(@address,1)) + 176 | convert(bigint, parseName(@address,2)) * 256 + 177 | convert(bigint, parseName(@address,3)) * 65536 + 178 | convert(bigint, parseName(@address,4)) * 16777216 179 | 180 | end 181 | go -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/SqlFraud.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EFBF2E31-31C0-4A8A-931C-BD086967AEDE} 8 | Exe 9 | Properties 10 | SqlFraud 11 | SqlFraud 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Dapper.1.42\lib\net45\Dapper.dll 37 | True 38 | 39 | 40 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.dll 41 | True 42 | 43 | 44 | ..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll 45 | True 46 | 47 | 48 | ..\packages\Serilog.Sinks.Literate.1.2.0\lib\net45\Serilog.Sinks.Literate.dll 49 | True 50 | 51 | 52 | ..\packages\Serilog.Sinks.MSSqlServer.2.0.14\lib\net45\Serilog.Sinks.MSSqlServer.dll 53 | True 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/SqlFraud/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /StructuredLogging/Demo/StructuredLoggingDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerGraphNet", "PowerGraphNet\PowerGraphNet.csproj", "{EB418329-F14A-4735-A69C-10A4174DB79A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlFraud", "SqlFraud\SqlFraud.csproj", "{EFBF2E31-31C0-4A8A-931C-BD086967AEDE}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeqEditor", "SeqEditor\SeqEditor.csproj", "{0C8E7448-22EF-423C-88D9-8827CD945B96}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {EB418329-F14A-4735-A69C-10A4174DB79A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {EB418329-F14A-4735-A69C-10A4174DB79A}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {EB418329-F14A-4735-A69C-10A4174DB79A}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {EB418329-F14A-4735-A69C-10A4174DB79A}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {EFBF2E31-31C0-4A8A-931C-BD086967AEDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {EFBF2E31-31C0-4A8A-931C-BD086967AEDE}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {EFBF2E31-31C0-4A8A-931C-BD086967AEDE}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {EFBF2E31-31C0-4A8A-931C-BD086967AEDE}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {0C8E7448-22EF-423C-88D9-8827CD945B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {0C8E7448-22EF-423C-88D9-8827CD945B96}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {0C8E7448-22EF-423C-88D9-8827CD945B96}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {0C8E7448-22EF-423C-88D9-8827CD945B96}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /StructuredLogging/Images/Morpheus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/Images/Morpheus.jpg -------------------------------------------------------------------------------- /StructuredLogging/Images/PowerGraphNet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/Images/PowerGraphNet.png -------------------------------------------------------------------------------- /StructuredLogging/Images/SeqEditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/Images/SeqEditor.png -------------------------------------------------------------------------------- /StructuredLogging/Images/SqlFraud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/Images/SqlFraud.png -------------------------------------------------------------------------------- /StructuredLogging/README.md: -------------------------------------------------------------------------------- 1 | # Structured Logging 2 | 3 | Introducing into structured logging and tools for .Net developers, such as Serilog and Seq. 4 | 5 | ![Better way to log](./Images/Morpheus.jpg) 6 | 7 | ## Public Talks 8 | `[2015-12-22]` [SpbDotNet, Saint Petersburg](https://spbdotnet.timepad.ru/event/276704/) ([Video](http://www.youtube.com/watch?v=f0UGDHT7ZwY), [SlideShare slides](http://www.slideshare.net/yu5k3/structured-logging), [PowerPoint slides](../../spbdotnet/StructuredLogging/StructuredLogging.pptx), [Code demos](../../spbdotnet/StructuredLogging/Demo)) 9 | `[2015-12-11]` [DotNext, Moscow](http://msk2015.dotnext.ru/talks/kulakov/) ([Video](https://www.youtube.com/watch?v=wy9YbBqhHqQ), [PowerPoint Slides](../../dotnext/StructuredLogging/StructuredLogging.pptx), [Code demos](../../dotnext/StructuredLogging/Demo)) 10 | 11 | ## Links 12 | - [Serilog site](http://serilog.net/) 13 | - [Seq site](https://getseq.net/) 14 | - [Nicholas Blumhardt blog](http://nblumhardt.com/) 15 | - [FSharp support for Serilog](https://github.com/destructurama/fsharp) 16 | - [JavaScript implementation of Serilog](https://github.com/structured-log/structured-log) 17 | -------------------------------------------------------------------------------- /StructuredLogging/StructuredLogging.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/StructuredLogging.pdf -------------------------------------------------------------------------------- /StructuredLogging/StructuredLogging.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kulakovt/Samples/f0775458e80e2c134b9710f3541a620a2d0ade12/StructuredLogging/StructuredLogging.pptx --------------------------------------------------------------------------------