├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── packages.yml
├── .gitignore
├── Build.csproj
├── DBScripts
├── MySQL.sql
├── PostgreSql.sql
└── SqlServer.sql
├── LICENSE-APACHE
├── LICENSE-MIT
├── NuGet.config
├── Readme.md
├── StackExchange.Exceptional.sln
├── StackExchange.Exceptional.sln.DotSettings
├── appveyor.yml
├── build.cmd
├── build.ps1
├── docs
├── 404.html
├── AddingLogData.md
├── AspDotNet.md
├── AspDotNetCore.md
├── ConsoleDotNet.md
├── ConsoleDotNetCore.md
├── Providers
│ ├── JSON.md
│ ├── Memory.md
│ ├── MySQL.md
│ ├── PostgreSql.md
│ └── SqlServer.md
├── Releases.md
├── Settings.md
├── UpgradeToV2.md
├── _config.yml
├── _includes
│ └── navigation.html
├── _layouts
│ └── default.html
├── css
│ └── main.css
├── images
│ ├── Command.png
│ ├── ErrorDetail.png
│ └── ErrorList.png
└── index.md
├── global.json
├── samples
├── Directory.Build.props
├── Directory.Packages.props
├── Samples.AspNetCore
│ ├── .bowerrc
│ ├── Controllers
│ │ ├── HomeController.cs
│ │ └── TestController.cs
│ ├── Program.cs
│ ├── Samples.AspNetCore.csproj
│ ├── Startup.cs
│ ├── Views
│ │ ├── Home
│ │ │ ├── Index.cshtml
│ │ │ └── Stacks.cshtml
│ │ ├── Shared
│ │ │ └── _Layout.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── bower.json
│ ├── bundleconfig.json
│ └── wwwroot
│ │ ├── css
│ │ ├── site.css
│ │ └── site.min.css
│ │ ├── favicon.ico
│ │ ├── images
│ │ ├── banner1.svg
│ │ ├── banner2.svg
│ │ ├── banner3.svg
│ │ └── banner4.svg
│ │ ├── js
│ │ ├── site.js
│ │ └── site.min.js
│ │ └── lib
│ │ ├── bootstrap
│ │ └── dist
│ │ │ ├── css
│ │ │ ├── bootstrap-theme.css
│ │ │ ├── bootstrap-theme.css.map
│ │ │ ├── bootstrap-theme.min.css
│ │ │ ├── bootstrap-theme.min.css.map
│ │ │ ├── bootstrap.css
│ │ │ ├── bootstrap.css.map
│ │ │ ├── bootstrap.min.css
│ │ │ └── bootstrap.min.css.map
│ │ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ └── glyphicons-halflings-regular.woff2
│ │ │ └── js
│ │ │ ├── bootstrap.js
│ │ │ ├── bootstrap.min.js
│ │ │ └── npm.js
│ │ └── jquery
│ │ └── dist
│ │ ├── jquery.js
│ │ ├── jquery.min.js
│ │ └── jquery.min.map
├── Samples.Console
│ ├── App.config
│ ├── Errors
│ │ └── ExceptionsGoHere.txt
│ ├── Program.cs
│ └── Samples.Console.csproj
├── Samples.ConsoleNetCore
│ ├── Errors
│ │ └── ExceptionsGoHere.txt
│ ├── Program.cs
│ ├── Samples.ConsoleNetCore.csproj
│ └── appsettings.json
└── Samples.MVC5
│ ├── App_Start
│ ├── BundleConfig.cs
│ ├── FilterConfig.cs
│ └── RouteConfig.cs
│ ├── Content
│ ├── Site.css
│ ├── errors.js
│ └── prettify.js
│ ├── Controllers
│ ├── HomeController.cs
│ └── TestController.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── Images
│ ├── accent.png
│ ├── bullet.png
│ └── heroAccent.png
│ ├── Properties
│ └── launchSettings.json
│ ├── Samples.MVC5.csproj
│ ├── Views
│ ├── Home
│ │ └── Index.cshtml
│ ├── Shared
│ │ ├── Error.cshtml
│ │ └── _Layout.cshtml
│ ├── Test
│ │ └── Form.cshtml
│ ├── Web.config
│ └── _ViewStart.cshtml
│ ├── Web.config
│ └── favicon.ico
├── src
├── Directory.Build.props
├── Directory.Packages.props
├── StackExchange.Exceptional.AspNetCore
│ ├── AspNetCoreExtensions.cs
│ ├── Exceptional.cs
│ ├── ExceptionalBuilderExtensions.cs
│ ├── ExceptionalMiddleware.cs
│ ├── ExceptionalServiceExtensions.cs
│ ├── ExceptionalSettings.cs
│ ├── ExceptionalStartupFilter.cs
│ └── StackExchange.Exceptional.AspNetCore.csproj
├── StackExchange.Exceptional.MongoDB
│ ├── MongoDBErrorStore.cs
│ └── StackExchange.Exceptional.MongoDB.csproj
├── StackExchange.Exceptional.MySQL
│ ├── MySQLErrorStore.cs
│ └── StackExchange.Exceptional.MySQL.csproj
├── StackExchange.Exceptional.PostgreSql
│ ├── PostgreSqlErrorStore.cs
│ └── StackExchange.Exceptional.PostgreSql.csproj
├── StackExchange.Exceptional.Shared
│ ├── Command.cs
│ ├── EmailSettings.cs
│ ├── Error.cs
│ ├── ErrorAfterLogEventArgs.cs
│ ├── ErrorBeforeLogEventArgs.cs
│ ├── ErrorStore.cs
│ ├── ErrorStoreSettings.cs
│ ├── ExceptionalSettingsExtensions.cs
│ ├── ExceptionalUtils.StackTrace.cs
│ ├── ExceptionalUtils.Test.cs
│ ├── ExceptionalUtils.cs
│ ├── Extensions.Handlers.cs
│ ├── Extensions.cs
│ ├── IExceptionalHandled.cs
│ ├── Internal
│ │ ├── CaseInsensitiveDictionaryConverter.cs
│ │ ├── Constants.cs
│ │ ├── ErrorEmail.cs
│ │ ├── ExceptionalSettingsBase.cs
│ │ ├── HtmlBase.cs
│ │ ├── IPNet.cs
│ │ ├── InternalExtensions.cs
│ │ ├── KnownHeaders.cs
│ │ ├── KnownRoutes.cs
│ │ ├── Resources.cs
│ │ ├── Statics.cs
│ │ └── StringBuilderCache.cs
│ ├── Notifiers
│ │ ├── EmailNotifier.cs
│ │ └── IErrorNotifier.cs
│ ├── Pages
│ │ ├── ErrorDetailPage.cs
│ │ ├── ErrorListPage.cs
│ │ └── WebPage.cs
│ ├── Resources
│ │ ├── Bundle.css
│ │ ├── Bundle.js
│ │ ├── Bundle.min.css
│ │ ├── Bundle.min.js
│ │ └── Source
│ │ │ ├── Scripts.js
│ │ │ ├── Styles.less
│ │ │ ├── highlight.less
│ │ │ ├── highlight.pack.js
│ │ │ ├── jquery-3.2.1.min.js
│ │ │ └── jquery.tablesorter.min.js
│ ├── StackExchange.Exceptional.Shared.csproj
│ ├── StackTraceSettings.cs
│ ├── Stores
│ │ ├── JSONErrorStore.cs
│ │ ├── MemoryErrorStore.cs
│ │ └── SQLErrorStore.cs
│ ├── bundleconfig.json
│ └── compilerconfig.json
└── StackExchange.Exceptional
│ ├── AspNetExtensions.cs
│ ├── ConfigSettings.cs
│ ├── Exceptional.cs
│ ├── ExceptionalAsyncHandler.cs
│ ├── ExceptionalModule.cs
│ ├── ExceptionalSettings.cs
│ ├── HandlerFactory.cs
│ └── StackExchange.Exceptional.csproj
├── tests
├── Directory.Build.props
├── Directory.Packages.props
├── StackExchange.Exceptional.Tests.AspNetCore
│ ├── AspNetCoreTest.cs
│ ├── Commands.cs
│ ├── Configs
│ │ ├── Full.json
│ │ ├── Storage.JSON.json
│ │ ├── Storage.MongoDB.json
│ │ ├── Storage.MySQL.json
│ │ ├── Storage.PostgreSql.json
│ │ └── Storage.SQL.json
│ ├── Configuration.cs
│ ├── Events.cs
│ ├── Handlers.cs
│ ├── LogFilters.cs
│ ├── Logging.cs
│ ├── Middleware.cs
│ ├── Pages.cs
│ ├── StackExchange.Exceptional.Tests.AspNetCore.csproj
│ ├── StartupFilter.cs
│ ├── Static.cs
│ └── StaticConfiguration.cs
└── StackExchange.Exceptional.Tests
│ ├── Basic.cs
│ ├── Helpers
│ ├── Attributes.cs
│ ├── BaseTest.cs
│ ├── Resource.cs
│ ├── Skip.cs
│ └── TestConfig.cs
│ ├── Ignore.cs
│ ├── InternalExtensionsTest.cs
│ ├── LegacyCompatTest.cs
│ ├── StackExchange.Exceptional.Tests.csproj
│ ├── Storage
│ ├── JSONErrorStoreTest.cs
│ ├── MemoryErrorStoreTest.cs
│ ├── MongoDBErrorStoreTest.cs
│ ├── MySQLErrorStoreTest.cs
│ ├── PostgreSQLErrorStoreTest.cs
│ ├── SQLErrorStoreTest.cs
│ └── StoreBaseTest.cs
│ └── TestExtensions.cs
└── version.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome:http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Don't use tabs for indentation.
7 | [*]
8 | indent_style = space
9 | # (Please don't specify an indent_size here; that has too many unintended consequences.)
10 |
11 | # Code files
12 | [*.{cs,csx,vb,vbx}]
13 | indent_size = 4
14 | insert_final_newline = true
15 | charset = utf-8-bom
16 |
17 | # Xml project files
18 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
19 | indent_size = 2
20 |
21 | # Xml config files
22 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
23 | indent_size = 2
24 |
25 | # JSON files
26 | [*.json]
27 | indent_size = 2
28 |
29 | # Dotnet code style settings:
30 | [*.{cs,vb}]
31 | # Sort using and Import directives with System.* appearing first
32 | dotnet_sort_system_directives_first = true
33 | # Avoid "this." and "Me." if not necessary
34 | dotnet_style_qualification_for_field = false:suggestion
35 | dotnet_style_qualification_for_property = false:suggestion
36 | dotnet_style_qualification_for_method = false:suggestion
37 | dotnet_style_qualification_for_event = false:suggestion
38 |
39 | # Use language keywords instead of framework type names for type references
40 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
41 | dotnet_style_predefined_type_for_member_access = true:suggestion
42 |
43 | # Suggest more modern language features when available
44 | dotnet_style_object_initializer = true:suggestion
45 | dotnet_style_collection_initializer = true:suggestion
46 | dotnet_style_coalesce_expression = true:suggestion
47 | dotnet_style_null_propagation = true:suggestion
48 | dotnet_style_explicit_tuple_names = true:suggestion
49 |
50 | # CSharp code style settings:
51 | [*.cs]
52 | # Prefer "var" everywhere
53 | #csharp_style_var_for_built_in_types = true:suggestion
54 | #csharp_style_var_when_type_is_apparent = false:suggestion
55 | #csharp_style_var_elsewhere = true:suggestion
56 |
57 | # Prefer method-like constructs to have a expression-body
58 | csharp_style_expression_bodied_methods = true:none
59 | csharp_style_expression_bodied_constructors = true:none
60 | csharp_style_expression_bodied_operators = true:none
61 |
62 | # Prefer property-like constructs to have an expression-body
63 | csharp_style_expression_bodied_properties = true:none
64 | csharp_style_expression_bodied_indexers = true:none
65 | csharp_style_expression_bodied_accessors = true:none
66 |
67 | # Suggest more modern language features when available
68 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
69 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
70 | csharp_style_inlined_variable_declaration = true:suggestion
71 | csharp_style_throw_expression = true:suggestion
72 | csharp_style_conditional_delegate_call = true:suggestion
73 |
74 | # Newline settings
75 | csharp_new_line_before_open_brace = all
76 | csharp_new_line_before_else = true
77 | csharp_new_line_before_catch = true
78 | csharp_new_line_before_finally = true
79 | csharp_new_line_before_members_in_object_initializers = true
80 | csharp_new_line_before_members_in_anonymous_types = true
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.github/workflows/packages.yml:
--------------------------------------------------------------------------------
1 | name: Package Build CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 | paths:
9 | - '*'
10 | - '!/docs/*' # Don't run workflow when files are only in the /docs directory
11 |
12 | jobs:
13 | build:
14 | name: Ubuntu
15 | runs-on: ubuntu-latest
16 | services:
17 | mongo:
18 | image: mongo
19 | ports:
20 | - 27017/tcp
21 | env:
22 | MONGO_INITDB_DATABASE: test
23 | postgres:
24 | image: postgres
25 | ports:
26 | - 5432/tcp
27 | env:
28 | POSTGRES_USER: postgres
29 | POSTGRES_PASSWORD: postgres
30 | POSTGRES_DB: test
31 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
32 | sqlserver:
33 | image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
34 | ports:
35 | - 1433/tcp
36 | env:
37 | ACCEPT_EULA: Y
38 | SA_PASSWORD: g0d4mm!tSQLServer
39 | mysql:
40 | image: mysql
41 | ports:
42 | - 3306/tcp
43 | env:
44 | MYSQL_ROOT_PASSWORD: root
45 | MYSQL_DATABASE: test
46 | steps:
47 | - name: Checkout code
48 | uses: actions/checkout@v1
49 | - name: .NET Build
50 | run: dotnet build Build.csproj -c Release /p:CI=true
51 | - name: .NET Test
52 | run: dotnet test Build.csproj -c Release --no-build /p:CI=true
53 | env:
54 | EnableTestLogging: true
55 | MongoDBConnectionString: mongodb://localhost:${{ job.services.mongo.ports[27017] }}/test
56 | MySQLConnectionString: server=localhost;Port=${{ job.services.mysql.ports[3306] }};Uid=root;Pwd=root;Database=test;Allow User Variables=true
57 | PostgreSqlConnectionString: Server=localhost;Port=${{ job.services.postgres.ports[5432] }};Database=test;User Id=postgres;Password=postgres;
58 | SQLServerConnectionString: Server=tcp:127.0.0.1,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=g0d4mm!tSQLServer;
59 | - name: .NET Lib Pack
60 | run: dotnet pack Build.csproj --no-build -c Release /p:Packing=true /p:PackageOutputPath=$PWD/.nupkgs /p:CI=true
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Visual Studio
3 | #################
4 |
5 | ## Ignore Visual Studio temporary files, build results, and
6 | ## files generated by popular Visual Studio add-ons.
7 |
8 | # User-specific files
9 | *.suo
10 | *.user
11 | *.sln.docstates
12 | *.binlog
13 | .vs/
14 | .vscode/
15 | **/Properties/launchSettings.json
16 | !samples/Samples.MVC5/Properties/launchSettings.json
17 | .idea/
18 |
19 | # Test Files
20 | error-*.json
21 |
22 | # Build results
23 | .nupkgs/
24 | [Dd]ebug/
25 | [Rr]elease/
26 | *_i.c
27 | *_p.c
28 | *.ilk
29 | *.meta
30 | *.obj
31 | *.pch
32 | *.pdb
33 | *.pgc
34 | *.pgd
35 | *.rsp
36 | *.sbr
37 | *.tlb
38 | *.tli
39 | *.tlh
40 | *.tmp
41 | *.vspscc
42 | .builds
43 | *.dotCover
44 |
45 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
46 | packages/
47 |
48 | # Visual Studio profiler
49 | *.psess
50 | *.vsp
51 |
52 | # ReSharper is a .NET coding add-in
53 | _ReSharper*
54 |
55 | # Click-Once directory
56 | publish
57 |
58 | # Others
59 | [Bb]in
60 | [Oo]bj
61 | sql
62 | TestResults
63 | *.Cache
64 | ClientBin
65 | stylecop.*
66 | ~$*
67 | *.dbmdl
68 | Generated_Code #added for RIA/Silverlight projects
69 |
70 | # Backup & report files from converting an old project file to a newer
71 | # Visual Studio version. Backup files are not needed, because we have git ;-)
72 | _UpgradeReport_Files/
73 | Backup*/
74 | UpgradeLog*.XML
75 |
76 | ############
77 | ## Windows
78 | ############
79 |
80 | # Windows image file caches
81 | Thumbs.db
82 |
83 | # Folder config file
84 | Desktop.ini
85 |
86 | # Mac crap
87 | .DS_Store
88 |
89 | docs/Gemfile.lock
90 | docs/.sass-cache/
91 | docs/_site/
92 | _site/
--------------------------------------------------------------------------------
/Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DBScripts/MySQL.sql:
--------------------------------------------------------------------------------
1 | /*
2 | MySQL setup script for Exceptional
3 | Run this script for creating the exceptions table
4 | It will also upgrade a V1 schema to V2, just run the full script.
5 | */
6 |
7 | CREATE TABLE IF NOT EXISTS Exceptions(
8 | Id bigint NOT NULL AUTO_INCREMENT,
9 | GUID char(36) NOT NULL,
10 | ApplicationName nvarchar(50) NOT NULL,
11 | MachineName nvarchar(50) NOT NULL,
12 | CreationDate datetime NOT NULL,
13 | `Type` nvarchar(100) NOT NULL,
14 | IsProtected tinyint(1) NOT NULL default 0,
15 | Host nvarchar(100) NULL,
16 | Url nvarchar(500) NULL,
17 | HTTPMethod nvarchar(10) NULL,
18 | IPAddress varchar(40) NULL,
19 | Source nvarchar(100) NULL,
20 | Message nvarchar(1000) NULL,
21 | Detail MEDIUMTEXT NULL,
22 | StatusCode int NULL,
23 | DeletionDate datetime NULL,
24 | FullJson MEDIUMTEXT NULL,
25 | ErrorHash int NULL,
26 | DuplicateCount int NOT NULL default 1,
27 | LastLogDate datetime NULL,
28 | Category nvarchar(100) NULL,
29 | PRIMARY KEY (Id)
30 | );
31 |
32 | SELECT IF (EXISTS(SELECT 1
33 | FROM INFORMATION_SCHEMA.STATISTICS
34 | WHERE TABLE_SCHEMA = DATABASE()
35 | AND TABLE_NAME = 'Exceptions'
36 | AND INDEX_NAME = 'IX_Exceptions_GUID_ApplicationName_DeletionDate_CreationDate')
37 | ,'Select ''Already There'''
38 | ,'CREATE INDEX `IX_Exceptions_GUID_ApplicationName_DeletionDate_CreationDate` ON `Exceptions`(`GUID`, `ApplicationName`, `DeletionDate`, `CreationDate` DESC);')
39 | INTO @a;
40 | PREPARE q1 FROM @a;
41 | EXECUTE q1;
42 | DEALLOCATE PREPARE q1;
43 |
44 | SELECT IF (EXISTS(SELECT 1
45 | FROM INFORMATION_SCHEMA.STATISTICS
46 | WHERE TABLE_SCHEMA = DATABASE()
47 | AND TABLE_NAME = 'Exceptions'
48 | AND INDEX_NAME = 'IX_Exceptions_ErrorHash_AppName_CreationDate_DelDate')
49 | ,'Select ''Already There'''
50 | ,'CREATE INDEX `IX_Exceptions_ErrorHash_AppName_CreationDate_DelDate` ON `Exceptions`(`ErrorHash`, `ApplicationName`, `CreationDate` DESC, `DeletionDate`);')
51 | INTO @a;
52 | PREPARE q1 FROM @a;
53 | EXECUTE q1;
54 | DEALLOCATE PREPARE q1;
55 |
56 | /* Begin V2 Schema changes */
57 |
58 | SELECT IF (EXISTS(SELECT 1
59 | FROM INFORMATION_SCHEMA.COLUMNS
60 | WHERE TABLE_SCHEMA = DATABASE()
61 | AND TABLE_NAME = 'Exceptions'
62 | AND COLUMN_NAME = 'LastLogDate')
63 | ,'Select ''Already There'''
64 | ,'ALTER TABLE `Exceptions` ADD LastLogDate datetime NULL;')
65 | INTO @a;
66 | PREPARE q1 FROM @a;
67 | EXECUTE q1;
68 | DEALLOCATE PREPARE q1;
69 |
70 | SELECT IF (EXISTS(SELECT 1
71 | FROM INFORMATION_SCHEMA.COLUMNS
72 | WHERE TABLE_SCHEMA = DATABASE()
73 | AND TABLE_NAME = 'Exceptions'
74 | AND COLUMN_NAME = 'Category')
75 | ,'Select ''Already There'''
76 | ,'ALTER TABLE `Exceptions` ADD Category nvarchar(100) NULL;')
77 | INTO @a;
78 | PREPARE q1 FROM @a;
79 | EXECUTE q1;
80 | DEALLOCATE PREPARE q1;
81 |
82 | SELECT IF (EXISTS(SELECT 1
83 | FROM INFORMATION_SCHEMA.COLUMNS
84 | WHERE TABLE_SCHEMA = DATABASE()
85 | AND TABLE_NAME = 'Exceptions'
86 | AND COLUMN_NAME = 'SQL')
87 | ,'ALTER TABLE `Exceptions` DROP COLUMN `SQL`;'
88 | ,'Select ''Already Gone''')
89 | INTO @a;
90 | PREPARE q1 FROM @a;
91 | EXECUTE q1;
92 | DEALLOCATE PREPARE q1;
--------------------------------------------------------------------------------
/DBScripts/PostgreSql.sql:
--------------------------------------------------------------------------------
1 | /*
2 | PostgeSQL setup script for Exceptional
3 | Run this script for creating the exceptions table
4 | It will also upgrade a V1 schema to V2, just run the full script.
5 | */
6 | CREATE TABLE IF NOT EXISTS "public"."Errors" (
7 | "Id" serial8 NOT NULL,
8 | "GUID" uuid NOT NULL,
9 | "ApplicationName" varchar(50) NOT NULL,
10 | "MachineName" varchar(50) NOT NULL,
11 | "CreationDate" timestamp NOT NULL,
12 | "Type" varchar(100) NOT NULL,
13 | "IsProtected" bool DEFAULT False NOT NULL,
14 | "Host" varchar(100),
15 | "Url" varchar(500),
16 | "HTTPMethod" varchar(10),
17 | "IPAddress" varchar(40),
18 | "Source" varchar(100),
19 | "Message" varchar(1000),
20 | "Detail" text,
21 | "StatusCode" int4,
22 | "DeletionDate" timestamp,
23 | "FullJson" text,
24 | "ErrorHash" int4,
25 | "DuplicateCount" int4 DEFAULT 1 NOT NULL,
26 | "LastLogDate" timestamp,
27 | "Category" varchar(100),
28 | PRIMARY KEY ("Id")
29 | ) WITH (OIDS=FALSE);
30 |
31 | CREATE UNIQUE INDEX IF NOT EXISTS "IX_Exceptions_GUID_ApplicationName_DeletionDate_CreationDate" ON "public"."Errors" ("GUID", "ApplicationName", "CreationDate" DESC, "DeletionDate");
32 |
33 | CREATE INDEX IF NOT EXISTS "IX_Exceptions_ErrorHash_ApplicationName_CreationDate_DeletionDa" ON "public"."Errors" ("ApplicationName", "CreationDate" DESC, "DeletionDate", "ErrorHash");
34 |
35 | CREATE INDEX IF NOT EXISTS "IX_Exceptions_ApplicationName_DeletionDate_CreationDate_Filtere" ON "public"."Errors" ("ApplicationName", "CreationDate" DESC, "DeletionDate") WHERE "DeletionDate" IS NULL;
36 |
37 | /* Begin V2 Schema changes */
38 |
39 | ALTER TABLE "public"."Errors" ADD COLUMN IF NOT EXISTS "LastLogDate" timestamp;
40 |
41 | ALTER TABLE "public"."Errors" ADD COLUMN IF NOT EXISTS "Category" varchar(100);
42 |
43 | ALTER TABLE "public"."Errors" DROP COLUMN IF EXISTS "SQL";
--------------------------------------------------------------------------------
/DBScripts/SqlServer.sql:
--------------------------------------------------------------------------------
1 | /*
2 | SQL Server setup script for Exceptional
3 | Run this script for creating the exceptions table
4 | It will also upgrade a V1 schema to V2, just run the full script.
5 | */
6 | If Not Exists (Select 1
7 | From INFORMATION_SCHEMA.TABLES
8 | Where [TABLE_SCHEMA] = 'dbo'
9 | And [TABLE_NAME] = 'Exceptions')
10 | Begin
11 | Create Table [dbo].[Exceptions](
12 | [Id] [bigint] Not Null Identity,
13 | [GUID] [uniqueidentifier] Not Null,
14 | [ApplicationName] [nvarchar](50) Not Null,
15 | [MachineName] [nvarchar](50) Not Null,
16 | [CreationDate] [datetime] Not Null,
17 | [Type] [nvarchar](100) Not Null,
18 | [IsProtected] [bit] Not Null Default(0),
19 | [Host] [nvarchar](100) Null,
20 | [Url] [nvarchar](500) Null,
21 | [HTTPMethod] [nvarchar](10) Null,
22 | [IPAddress] [varchar](40) Null,
23 | [Source] [nvarchar](100) Null,
24 | [Message] [nvarchar](1000) Null,
25 | [Detail] [nvarchar](max) Null,
26 | [StatusCode] [int] Null,
27 | [DeletionDate] [datetime] Null,
28 | [FullJson] [nvarchar](max) Null,
29 | [ErrorHash] [int] Null,
30 | [DuplicateCount] [int] Not Null Default(1),
31 | [LastLogDate] [datetime] Null,
32 | [Category] nvarchar(100) Null
33 | Constraint [PK_Exceptions] Primary Key Clustered ([Id] Asc)
34 | With (Pad_Index = Off, Statistics_NoRecompute = Off, Ignore_Dup_Key = Off, Allow_Row_Locks = On, Allow_Page_Locks = On) On [PRIMARY]
35 | );
36 | End
37 |
38 | If Not Exists (Select 1 From sys.indexes Where object_id = OBJECT_ID('dbo.Exceptions') And name = 'IX_Exceptions_GUID_ApplicationName_DeletionDate_CreationDate')
39 | Begin
40 | Create Unique Nonclustered Index [IX_Exceptions_GUID_ApplicationName_DeletionDate_CreationDate] On [dbo].[Exceptions]
41 | (
42 | [GUID] Asc,
43 | [ApplicationName] Asc,
44 | [DeletionDate] Asc,
45 | [CreationDate] Desc
46 | );
47 | End
48 |
49 | If Not Exists (Select 1 From sys.indexes Where object_id = OBJECT_ID('dbo.Exceptions') And name = 'IX_Exceptions_ErrorHash_ApplicationName_CreationDate_DeletionDate')
50 | Begin
51 | Create Nonclustered Index [IX_Exceptions_ErrorHash_ApplicationName_CreationDate_DeletionDate] On [dbo].[Exceptions]
52 | (
53 | [ErrorHash] Asc,
54 | [ApplicationName] Asc,
55 | [CreationDate] Desc,
56 | [DeletionDate] Asc
57 | );
58 | End
59 |
60 | If Not Exists (Select 1 From sys.indexes Where object_id = OBJECT_ID('dbo.Exceptions') And name = 'IX_Exceptions_ApplicationName_DeletionDate_CreationDate_Filtered')
61 | Begin
62 | Create Nonclustered Index [IX_Exceptions_ApplicationName_DeletionDate_CreationDate_Filtered] On [dbo].[Exceptions]
63 | (
64 | [ApplicationName] Asc,
65 | [DeletionDate] Asc,
66 | [CreationDate] Desc
67 | )
68 | Where DeletionDate Is Null;
69 | End
70 |
71 | If Not Exists (Select 1 From sys.indexes Where object_id = OBJECT_ID('dbo.Exceptions') And name = 'IX_Exceptions_CreationDate_Includes')
72 | Begin
73 | Create Nonclustered Index [IX_Exceptions_CreationDate_Includes] On [dbo].[Exceptions]
74 | (
75 | [CreationDate] Asc
76 | )
77 | Include ([ApplicationName], [MachineName], [DuplicateCount])
78 | End
79 |
80 | /* Begin V2 Schema changes */
81 |
82 | If Not Exists (Select 1 From INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME = 'Exceptions' And COLUMN_NAME = 'LastLogDate')
83 | Begin
84 | Alter Table [dbo].[Exceptions] Add [LastLogDate] [datetime] Null;
85 | End
86 |
87 | If Not Exists (Select 1 From INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME = 'Exceptions' And COLUMN_NAME = 'Category')
88 | Begin
89 | Alter Table [dbo].[Exceptions] Add [Category] nvarchar(100) Null;
90 | End
91 |
92 | If Exists (Select 1 From INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME = 'Exceptions' And COLUMN_NAME = 'SQL')
93 | Begin
94 | Alter Table [dbo].[Exceptions] Drop Column [SQL];
95 | End
96 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Copyright 2017 Nick Craver
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/StackExchange.Exceptional.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | GUID
3 | HTTP
4 | IP
5 | JSON
6 | SQL
7 | SSL
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2022
2 |
3 | skip_branch_with_pr: true
4 | skip_tags: true
5 | skip_commits:
6 | files:
7 | - '**/*.md'
8 |
9 | environment:
10 | Appveyor: true
11 | # Postgres
12 | POSTGRES_PATH: C:\Program Files\PostgreSQL\16
13 | PGUSER: postgres
14 | PGPASSWORD: Password12!
15 | POSTGRES_ENV_POSTGRES_USER: postgres
16 | POSTGRES_ENV_POSTGRES_PASSWORD: Password12!
17 | POSTGRES_ENV_POSTGRES_DB: test
18 | PostgreSqlConnectionString: Server=localhost;Port=5432;Database=test;User Id=postgres;Password=Password12!;
19 | # MySQL
20 | MYSQL_PATH: C:\Program Files\MySQL\MySQL Server 8.0
21 | MYSQL_PWD: Password12!
22 | MYSQL_ENV_MYSQL_USER: root
23 | MYSQL_ENV_MYSQL_PASSWORD: Password12!
24 | MYSQL_ENV_MYSQL_DATABASE: test
25 | MySQLConnectionString: server=localhost;uid=root;pwd=Password12!;database=test;Allow User Variables=true
26 | # MongoDB
27 | MongoDBConnectionString: mongodb://localhost/test
28 | # SQL Server
29 | SQLServerConnectionString: Server=(local)\SQL2019;Database=master;User ID=sa;Password=Password12!;Encrypt=False
30 |
31 | services:
32 | - mongodb
33 |
34 | init:
35 | - git config --global core.autocrlf input
36 | - SET PATH=%POSTGRES_PATH%\bin;%MYSQL_PATH%\bin;%PATH%
37 | - net start MSSQL$SQL2019
38 | - net start postgresql-x64-16
39 | - ps: Start-Service MySQL80
40 |
41 | nuget:
42 | disable_publish_on_pr: true
43 |
44 | build_script:
45 | # Postgres
46 | - createdb test
47 | # MySQL
48 | - mysql -e "create database test;" --user=root
49 | - ps: .\build.ps1 -PullRequestNumber "$env:APPVEYOR_PULL_REQUEST_NUMBER" -CreatePackages $true
50 |
51 | test: off
52 | artifacts:
53 | - path: .\.nupkgs\*.nupkg
54 |
55 | deploy:
56 | - provider: NuGet
57 | server: https://www.myget.org/F/stackoverflow/api/v2
58 | on:
59 | branch: main
60 | api_key:
61 | secure: P/UHxq2DEs0GI1SoDXDesHjRVsSVgdywz5vmsnhFQQY5aJgO3kP+QfhwfhXz19Rw
62 | symbol_server: https://www.myget.org/F/stackoverflow/symbols/api/v2/package
63 | - provider: NuGet
64 | server: https://www.myget.org/F/exceptional/api/v2
65 | on:
66 | branch: main
67 | api_key:
68 | secure: OQIXTmnq9eLRDtbatTQhSx4VE4pNdR7JbEVR7BYg8LmyOqcVcWMB/G316I17e61X
69 | symbol_server: https://www.myget.org/F/exceptional/api/v2/package
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE"
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding(PositionalBinding=$false)]
2 | param(
3 | [bool] $CreatePackages,
4 | [bool] $RunTests = $true,
5 | [string] $PullRequestNumber
6 | )
7 |
8 | Write-Host "Run Parameters:" -ForegroundColor Cyan
9 | Write-Host " CreatePackages: $CreatePackages"
10 | Write-Host " RunTests: $RunTests"
11 | Write-Host " dotnet --version:" (dotnet --version)
12 |
13 | $packageOutputFolder = "$PSScriptRoot\.nupkgs"
14 |
15 | if ($PullRequestNumber) {
16 | Write-Host "Building for a pull request (#$PullRequestNumber), skipping packaging." -ForegroundColor Yellow
17 | $CreatePackages = $false
18 | }
19 |
20 | Write-Host "Building all projects (Build.csproj traversal)..." -ForegroundColor "Magenta"
21 | dotnet build ".\Build.csproj" -c Release /p:CI=true
22 | Write-Host "Done building." -ForegroundColor "Green"
23 |
24 | if ($RunTests) {
25 | Write-Host "Running tests: Build.csproj traversal (all frameworks)" -ForegroundColor "Magenta"
26 | dotnet test ".\Build.csproj" -c Release --no-build --logger trx
27 | if ($LastExitCode -ne 0) {
28 | Write-Host "Error with tests, aborting build." -Foreground "Red"
29 | Exit 1
30 | }
31 | Write-Host "Tests passed!" -ForegroundColor "Green"
32 | }
33 |
34 | if ($CreatePackages) {
35 | mkdir -Force $packageOutputFolder | Out-Null
36 | Write-Host "Clearing existing $packageOutputFolder..." -NoNewline
37 | Get-ChildItem $packageOutputFolder | Remove-Item
38 | Write-Host "done." -ForegroundColor "Green"
39 |
40 | Write-Host "Building all packages" -ForegroundColor "Green"
41 | dotnet pack ".\Build.csproj" --no-build -c Release /p:Packing=true /p:PackageOutputPath=$packageOutputFolder /p:CI=true
42 | }
43 |
44 | Write-Host "Done."
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
404
6 |
7 |
Page not found :(
8 |
The requested page could not be found.
9 |
--------------------------------------------------------------------------------
/docs/AddingLogData.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Adding Log Data'
4 | ---
5 | ## Adding Log Data
6 |
7 | An exception logger wouldn't be very awesome if you couldn't add some data when you log an exception. Exceptional allows you to do this in several ways.
8 |
9 | **Extension** `.AddLogData(string key, string value)`: This extension allows adding of key/value pairs when logging an exception. This is useful for logging which server was being hit, what the count in a queue was, etc. Basically any infomation you may think is relevant for debugging. The simplest usage is:
10 | ```c#
11 | new Exception("Oops.")
12 | .AddLogData("MyKey", "MyValue")
13 | .Log();
14 | ```
15 |
16 | **Setting** `Settings.GetCustomData`: This hook lets you add custom data (*if it's not already provided*) to an exception while logging. Example usage:
17 | ```c#
18 | Exceptional.Settings.GetCustomData = (exception, data) =>
19 | {
20 | data.Add("Example string", DateTime.UtcNow.ToString());
21 | data.Add("User Id", "You could fetch a user/account Id here, etc.");
22 | data.Add("Links get linkified", "https://www.google.com");
23 | };
24 | ```
25 |
26 | **Extension/Setting** `.AddHandler(Action)` (and `.AddHandler(string, Action)`): Any handlers added execute for each exception (including inner exceptions) that are thrown. It's very useful for having a single place to handle adding additional data based on the command type. Any custom system you are using can add data here.
27 |
28 | **Interface** `IExceptionalHandled`: This is an interface your custom exception types can implement. If it does implement it, it's called upon logging (again, even if it's an inner exception). This approach let's you keep all the logging of additional useful data for an exception with the definition. Here's an example:
29 | ```c#
30 | public class RedisException : Exception, IExceptionalHandled
31 | {
32 | public RedisException(string message) : base(message) { }
33 |
34 | public void ExceptionalHandler(Error e)
35 | {
36 | var cmd = e.AddCommand(new Command("Redis"));
37 | foreach (string k in e.Exception.Data.Keys) // e.Exception == this
38 | {
39 | var val = e.Exception.Data[k] as string;
40 | if (k == "redis-command") cmd.CommandString = val;
41 | if (k.StartsWith("Redis-")) cmd.AddData(k.Substring("Redis-".Length), val);
42 | }
43 | }
44 | }
45 | ```
46 |
47 | #### Commands
48 |
49 | Commands are a V2 feature and replace the V1 SQL-only logging field. Commands have a type (a title/description), a command string (e.g. the SQL query), and a key/value store for any relevant data you want to associate there, e.g. the SQL Server it was hitting, the timeout, etc.
50 |
51 | Here's an example of how a command is added in a handler (this example is one of [the default handlers](https://github.com/NickCraver/StackExchange.Exceptional/blob/main/src/StackExchange.Exceptional.Shared/Extensions.Handlers.cs)):
52 |
53 | ```c#
54 | Handlers.AddHandler((e, se) =>
55 | {
56 | if (se.Data == null) return;
57 | e.AddCommand(new Command("SQL Server Query", se.Data.Contains("SQL") ? se.Data["SQL"] as string : null)
58 | .AddData(nameof(se.Server), se.Server)
59 | .AddData(nameof(se.Number), se.Number.ToString())
60 | .AddData(nameof(se.LineNumber), se.LineNumber.ToString())
61 | .AddData(se.Procedure.HasValue(), nameof(se.Procedure), se.Procedure)
62 | );
63 | });
64 | ```
65 |
66 | Each command is rendered as a section in the detail view, like this:
67 | 
--------------------------------------------------------------------------------
/docs/AspDotNet.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'ASP.NET (non-Core)'
4 | ---
5 | ## ASP.NET (non-Core)
6 |
7 | Install [the `StackExchange.Exceptional` nuget package](https://www.nuget.org/packages/StackExchange.Exceptional) via:
8 |
9 | ```powershell
10 | Install-Package StackExchange.Exceptional
11 | ```
12 |
13 | **If setting up a web application, I encourage you to [check out the ASP.NET MVC 5 sample project](https://github.com/NickCraver/StackExchange.Exceptional/tree/main/samples/Samples.MVC5), it has all of the below in a proper context.**
14 |
15 | Web.Config example pieces for an IIS 7.5 deployment:
16 |
17 | ```xml
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 | This is all optional (except `StackExchange.Exceptional.ExceptionalModule` if you want to capture unhandled MVC exceptions automatically), you can setup completely via code as well. Examples:
47 |
48 | ```c#
49 | Exceptional.Configure(settings => settings.DefaultStore = new SQLErrorStore(applicationName: "My Application", connectionString: _connectionString));
50 | ```
51 |
52 | ...then to log exceptions (context would be null for non-web applications):
53 |
54 | ```c#
55 | exception.Log(_context);
56 | ```
57 |
58 | #### Optional Configuration
59 |
60 | Now for the optional pieces, Stack Overflow exposes the error handler through an MVC route, this allows you to lock it down using whatever security you already have in place:
61 |
62 | ```c#
63 | [Route("admin/errors/{resource?}/{subResource?}")]
64 | public Task Exceptions() => ExceptionalModule.HandleRequestAsync(System.Web.HttpContext.Current);
65 | ```
66 |
67 | If you want to customize the views (adding links, etc.) you can add JavaScript files which will be included on both the exception list and exception detail views. In the exception detail view parsing is not necessary since all of the detail is available via `window.Exception` as well:
68 |
69 | ```c#
70 | Exceptional.Settings.Render.JSIncludes.Add("/Content/errors.js");
71 | ```
72 |
73 | If you want to store some custom key/value style data with an exception, you can set up `Settings.GetCustomData`, for example:
74 |
75 | ```c#
76 | Exceptional.Settings.GetCustomData = (exception, data) =>
77 | {
78 | // exception is the exception thrown
79 | // data is a Dictionary to add custom data too
80 | data.Add("Example string", DateTime.UtcNow.ToString());
81 | data.Add("User Id", "You could fetch a user/account Id here, etc.");
82 | data.Add("Links get linkified", "https://www.google.com");
83 | };
84 | ```
85 | ...and these pairs will appear on the error detail screen in a "Custom" section.
86 |
87 | #### Routes
88 |
89 | For convenience, there is an easy to add route handler to render your errors. This way you can lock down the route using your current security models. The route iself is simple, in any controller (probably an admin or localhost controller):
90 |
91 | ```c#
92 | public Task Exceptions() => ExceptionalModule.HandleRequestAsync(System.Web.HttpContext.Current);
93 | ```
94 |
--------------------------------------------------------------------------------
/docs/AspDotNetCore.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'ASP.NET Core'
4 | ---
5 | ## ASP.NET Core
6 |
7 | Install [the `StackExchange.Exceptional.AspNetCore` nuget package](https://www.nuget.org/packages/StackExchange.Exceptional.AspNetCore) via:
8 |
9 | ```powershell
10 | Install-Package StackExchange.Exceptional.AspNetCore
11 | ```
12 |
13 | **If setting up a web application, I encourage you to [check out the ASP.NET Core sample project](https://github.com/NickCraver/StackExchange.Exceptional/tree/main/samples/Samples.AspNetCore), it has all of the below in a proper context.**
14 |
15 | #### Configuration
16 |
17 | You register Exceptional by adding it in your `Startup.ConfigureServices()` method. A few overloads are available:
18 |
19 | ```c#
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | // This uses all defaults (e.g. the in-memory error store)
23 | services.AddExceptional(settings =>
24 | {
25 | settings.Store.ApplicationName = "Samples.AspNetCore";
26 | });
27 | }
28 | ```
29 |
30 | ...or the other `.AddExceptional()` overloads:
31 | ```c#
32 | // This uses all defaults (e.g. the in-memory error store)
33 | services.AddExceptional(Configuration.GetSection("Exceptional"));
34 | ```
35 | and configure Exceptional in your `Configuration`, e.g. in your `appsettings.json` ([full schema here](https://github.com/NickCraver/StackExchange.Exceptional/blob/main/samples/Samples.AspNetCore/appsettings.json)):
36 | ```json
37 | {
38 | "Exceptional": {
39 | "Store": {
40 | "ApplicationName": "Samples (ASP.NET Core)",
41 | "Type": "SQL",
42 | "ConnectionString": "Server=.;Database=Local.Exceptions;Trusted_Connection=True;"
43 | }
44 | }
45 | ```
46 | ...or you can use a combination of the two:
47 | ```c#
48 | services.AddExceptional(Configuration.GetSection("Exceptional"), settings =>
49 | {
50 | settings.UseExceptionalPageOnThrow = HostingEnvironment.IsDevelopment();
51 | });
52 | ```
53 | Note the `UseExceptionalPageOnThrow` property here. This is the Exceptional alternative to `app.UseDeveloperExceptionPage();` to view exceptions as they happen locally in a useful/familiar format.
54 |
55 | #### Middleware
56 |
57 | To add the Exceptional middleware for handling errors, add it to your `Startup.Configure()` method:
58 | ```c#
59 | public void Configure(IApplicationBuilder app)
60 | {
61 | app.UseExceptional();
62 | }
63 | ```
64 |
65 | Note that you should call this before anything you want handled, as exceptions will "bubble up" to this point. For example, you almost certainly want this called **before** `app.UseMvc();`, so that any errors MVC (or your code running inside it) throws are handled.
66 |
67 | #### Routes
68 |
69 | For convenience, there is an easy to add route handler to render your errors. This way you can lock down the route using your current security models. The route iself is simple, in any controller (probably an admin or localhost controller):
70 |
71 | ```c#
72 | public async Task Exceptions() => await ExceptionalMiddleware.HandleRequestAsync(HttpContext);
73 | ```
74 |
--------------------------------------------------------------------------------
/docs/ConsoleDotNet.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: '.NET Console App (non-Core)'
4 | ---
5 | ## .NET Console Applications (non-Core)
6 |
7 | Install [the `StackExchange.Exceptional` nuget package](https://www.nuget.org/packages/StackExchange.Exceptional) via:
8 |
9 | ```powershell
10 | Install-Package StackExchange.Exceptional
11 | ```
12 |
13 | **If setting up a console application, I encourage you to [check out the Console sample project](https://github.com/NickCraver/StackExchange.Exceptional/tree/main/samples/Samples.Console), it has all of the below in a proper context.**
14 |
15 |
16 | App.Config example pieces:
17 |
18 | ```xml
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ```
41 |
42 | This is all optional, you can setup completely via code as well. Examples:
43 |
44 | ```c#
45 | Exceptional.Configure(new ExceptionalSettings() { DefaultStore = new SQLErrorStore(_connectionString,"My Application") });
46 | ```
47 |
48 | ...then to log exceptions:
49 |
50 | ```c#
51 | exception.LogNoContext();
52 | ```
53 |
54 | #### Optional Configuration
55 |
56 |
57 | If you want to store some custom key/value style data with an exception, you can use `.AddLogData` extension method, for example:
58 |
59 | ```c#
60 | exception.AddLogData("Example string", DateTime.UtcNow.ToString())
61 | .AddLogData("User Id", "You could fetch a user/account Id here, etc.")
62 | .AddLogData("Links get linkified", "https://www.google.com");
63 | ```
64 | ...and these pairs will appear on the error detail screen in a "Custom" section and in the `CustomData` dictionary of `Exceptional.Error`.
65 |
--------------------------------------------------------------------------------
/docs/ConsoleDotNetCore.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: '.NET Core Console App'
4 | ---
5 | ## .NET Core Console Applications
6 |
7 | Install [the `StackExchange.Exceptional.AspNetCore` nuget package](https://www.nuget.org/packages/StackExchange.Exceptional.AspNetCore) via:
8 |
9 | ```powershell
10 | Install-Package StackExchange.Exceptional.AspNetCore
11 | ```
12 |
13 | **If setting up a console application, I encourage you to [check out the .NET Core Console sample project](https://github.com/NickCraver/StackExchange.Exceptional/tree/main/samples/Samples.NetCoreConsole), it has all of the below in a proper context.**
14 |
15 | You can either configure things via a config file, for example `appsettings.json`:
16 | ```json
17 | {
18 | "Exceptional": {
19 | "Store": {
20 | "ApplicationName": "Samples (ASP.NET Core)",
21 | "Type": "SQL",
22 | "ConnectionString": "Server=.;Database=Local.Exceptions;Trusted_Connection=True;"
23 | }
24 | }
25 | ```
26 | ...and hook up that configuration at startup (this is an example - there are many ways to do this):
27 | ```c#
28 | var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
29 | var exceptionalSettings = config.GetSection("Exceptional").Get();
30 | Exceptional.Configure(exceptionalSettings);
31 | ```
32 |
33 | Or, you can opt to configure entirely through code instead:
34 |
35 | ```c#
36 | Exceptional.Configure(new ExceptionalSettings() { DefaultStore = new SQLErrorStore(_connectionString,"My Application") });
37 | ```
38 |
39 | ...then to log exceptions:
40 |
41 | ```c#
42 | exception.LogNoContext();
43 | ```
44 |
45 | #### Optional Configuration
46 |
47 | If you want to store some custom key/value style data with an exception, you can use `.AddLogData` extension method, for example:
48 |
49 | ```c#
50 | exception.AddLogData("Example string", DateTime.UtcNow.ToString())
51 | .AddLogData("User Id", "You could fetch a user/account Id here, etc.")
52 | .AddLogData("Links get linkified", "https://www.google.com");
53 | ```
54 | ...and these pairs will appear on the error detail screen in a "Custom" section and in the `CustomData` dictionary of `Exceptional.Error`.
55 |
--------------------------------------------------------------------------------
/docs/Providers/JSON.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Storage: JSON'
4 | ---
5 | ## Storage: JSON
6 |
7 | The JSON error store is file-based and not the most performant for high throughput situations (due to the performance of file systems in general). It's just fine for smaller applications though.
8 |
9 | #### Installation
10 | No install required, it's built-in.
11 |
12 | #### Coniguration
13 | Web.config example:
14 | ```xml
15 |
16 | ```
17 |
18 | ASP.NET Core JSON example:
19 | ```json
20 | {
21 | "Exceptional": {
22 | "ErrorStore": {
23 | "ApplicationName": "Samples (ASP.NET Core)",
24 | "Type": "JSON",
25 | "Path": "/Errors",
26 | "Size": 200
27 | }
28 | }
29 | }
30 | ```
31 |
32 | C# Code example:
33 | ```c#
34 | Exceptional.Configure(new JSONErrorStore("Errors", 200));
35 | ```
--------------------------------------------------------------------------------
/docs/Providers/Memory.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Storage: Memory'
4 | ---
5 | ## Storage: Memory
6 |
7 | The memory store is by nature volatile and app-domain specific. This means it's not ideal for a web farm or services that restart often since viewing them will be more of a burden. A centralized store like SQL Server (or anything!) is recommended for these cases.
8 |
9 | #### Installation
10 | No install required, it's built-in and is the default store.
11 |
12 | #### Coniguration
13 | Web.config example:
14 | ```xml
15 |
16 | ```
17 |
18 | ASP.NET Core JSON example:
19 | ```json
20 | {
21 | "Exceptional": {
22 | "Store": {
23 | "ApplicationName": "Samples (ASP.NET Core)",
24 | "Type": "Memory",
25 | "Size": 500
26 | }
27 | }
28 | }
29 | ```
30 |
31 | C# Code example:
32 | ```c#
33 | // Do nothing! It's the default.
34 | ```
--------------------------------------------------------------------------------
/docs/Providers/MySQL.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Storage: MySQL'
4 | ---
5 | ## Storage: MySQL
6 |
7 | #### Installation
8 | Install for MySQL is as follows:
9 |
10 | 1. Install [the `StackExchange.Exceptional.MySQL` package](https://www.nuget.org/packages/StackExchange.Exceptional.MySQL).
11 | 2. Run the [SQL to create the Exceptions table][MySQL].
12 | 3. Configure the application to use this error store.
13 |
14 | #### Coniguration
15 | Web.config example:
16 | ```xml
17 |
18 | ```
19 |
20 | ASP.NET Core JSON example:
21 | ```json
22 | {
23 | "Exceptional": {
24 | "Store": {
25 | "ApplicationName": "Samples (ASP.NET Core)",
26 | "Type": "MySQL",
27 | "ConnectionString": "Data Source=.;Initial Catalog=Exceptions;Uid=Exceptions;Pwd=iloveerrors"
28 | }
29 | }
30 | }
31 | ```
32 |
33 | C# Code example:
34 | ```c#
35 | Exceptional.Configure("My Application", new MySQLErrorStore("Data Source=.;Initial Catalog=Exceptions;Uid=Exceptions;Pwd=iloveerrors"));
36 | ```
37 |
38 | [MySQL]: https://github.com/NickCraver/StackExchange.Exceptional/blob/main/DBScripts/MySQL.sql
--------------------------------------------------------------------------------
/docs/Providers/PostgreSql.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Storage: PostgreSql'
4 | ---
5 | ## Storage: PostgreSql
6 |
7 | #### Installation
8 | Install for PostgreSql is as follows:
9 |
10 | 1. Install [the `StackExchange.Exceptional.PostgreSql` package](https://www.nuget.org/packages/StackExchange.Exceptional.PostgreSql).
11 | 2. Run the [SQL to create the Exceptions table][PostgreSql].
12 | 3. Configure the application to use this error store.
13 |
14 | #### Coniguration
15 | Web.config example:
16 | ```xml
17 |
18 | ```
19 |
20 | ASP.NET Core JSON example:
21 | ```json
22 | {
23 | "Exceptional": {
24 | "Store": {
25 | "ApplicationName": "Samples (ASP.NET Core)",
26 | "Type": "PostgreSql",
27 | "ConnectionString": "Server=..."
28 | }
29 | }
30 | }
31 | ```
32 |
33 | C# Code example:
34 | ```c#
35 | Exceptional.Configure("My Application", new PostgreSqlErrorStore("Server=..."));
36 | ```
37 |
38 | [PostgreSql]: https://github.com/NickCraver/StackExchange.Exceptional/blob/main/DBScripts/PostgreSql.sql
--------------------------------------------------------------------------------
/docs/Providers/SqlServer.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: 'Storage: SQL Server'
4 | ---
5 | ## Storage: SQL Server
6 |
7 | Install for SQL Server is fairly straightforward, you'll need to do the following:
8 |
9 | 1. Give the application permissions to a database.
10 | 2. Run the [SQL to create the Exceptions table][SqlServer].
11 | 3. Configure the application to use this error store, either [in the web.config](https://github.com/NickCraver/StackExchange.Exceptional/wiki/Setup) or in code.
12 |
13 | #### Coniguration
14 | Web.config example:
15 | ```xml
16 |
17 | ```
18 |
19 | ASP.NET Core JSON example:
20 | ```json
21 | {
22 | "Exceptional": {
23 | "Store": {
24 | "ApplicationName": "Samples (ASP.NET Core)",
25 | "Type": "SQL",
26 | "ConnectionString": "Data Source=.;Initial Catalog=Exceptions;Uid=Exceptions;Pwd=iloveerrors"
27 | }
28 | }
29 | }
30 | ```
31 |
32 | C# Code example:
33 | ```c#
34 | Exceptional.Configure("My Application", new SQLErrorStore("Data Source=.;Initial Catalog=Exceptions;Uid=Exceptions;Pwd=iloveerrors"));
35 | ```
36 |
37 | [SqlServer]: https://github.com/NickCraver/StackExchange.Exceptional/blob/main/DBScripts/SqlServer.sql
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: 'Exceptional'
2 | subtitle: 'Documentation'
3 | codeurl: 'https://github.com/NickCraver/StackExchange.Exceptional'
4 | baseurl: '/StackExchange.Exceptional'
5 | highlighter: rouge
6 | exclude: ['README.md']
7 | markdown: kramdown
8 | redcarpet:
9 | extensions: [
10 | 'no_intra_emphasis',
11 | 'fenced_code_blocks',
12 | 'autolink',
13 | 'strikethrough',
14 | 'superscript',
15 | 'with_toc_data',
16 | 'tables',
17 | 'hardwrap'
18 | ]
19 |
--------------------------------------------------------------------------------
/docs/_includes/navigation.html:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/images/Command.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/docs/images/Command.png
--------------------------------------------------------------------------------
/docs/images/ErrorDetail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/docs/images/ErrorDetail.png
--------------------------------------------------------------------------------
/docs/images/ErrorList.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/docs/images/ErrorList.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 | ## StackExchange.Exceptional
5 | StackExchange.Exceptional is the error handler used internally by [Stack Exchange](http://stackexchange.com) and [Stack Overflow](http://stackoverflow.com) for logging to SQL (SQL Server and MySQL are both supported).
6 |
7 | It also supports JSON and memory error stores, filtering of exceptions before logging, and fail/retry mechanisms for storing errors if there's an interruption in connecting to the error store.
8 |
9 | #### Configuration
10 |
11 | - ASP.NET
12 | - [Getting started with ASP.NET (non-Core)]({{ site.baseurl }}/AspDotNet)
13 | - [Getting started with ASP.NET Core]({{ site.baseurl }}/AspDotNetCore)
14 | - Other .NET (eg: console application)
15 | - [Getting started with .NET (non-Core) for a console app]({{ site.baseurl }}/ConsoleDotNet)
16 | - [Getting started with .NET Core for a console app]({{ site.baseurl }}/ConsoleDotNetCore)
17 |
18 | #### Details
19 | While having some features centered around logging/showing exceptions from web applications, **it can be used with either web or console applications**. `HttpContext` is optional when logging exceptions.
20 | An example use of this at Stack Exchange is windows services logging to SQL and viewed elsewhere in a central dashboard called [Opserver](https://github.com/opserver/Opserver).
21 |
22 | This project was inspired by [ELMAH](https://code.google.com/p/elmah/), but it didn't suit our particular needs for very, very high volume error logging when a network-level event occurs.
23 |
24 | Stack Exchange needed a handful things in an error handler/logger:
25 |
26 | - High speed/capacity logging (on the order of 100,000/min)
27 | - Handling the connection to a central error store being interrupted (without losing the errors)
28 | - Add custom data to exceptions
29 | - Rolling up of duplicate errors
30 |
31 | Given the above needs, StackExchange.Exceptional was created. It's as lightweight as possible to suit the needs of the network, but if there are compelling features I'll definitely look at adding them to the main repo here and NuGet soon.
32 |
33 | Here are some examples of what Exceptional looks like.
34 |
35 | Error listing:
36 | 
37 |
38 | Error Detail:
39 | 
40 |
41 | You can quickly add these routes to [ASP.NET (non-Core)]({{ site.baseurl }}/AspDotNet#routes) or [ASP.NET Core]({{ site.baseurl }}/AspDotNetCore#routes).
42 |
43 |
44 | #### License
45 |
46 | Dual-licensed under:
47 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
48 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
49 |
50 | 
51 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "msbuild-sdks": {
3 | "MSBuild.SDK.SystemWeb" : "4.0.93"
4 | },
5 | "sdk": {
6 | "allowPrerelease": false
7 | }
8 | }
--------------------------------------------------------------------------------
/samples/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
--------------------------------------------------------------------------------
/samples/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "wwwroot/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.AspNetCore.Mvc;
5 | using StackExchange.Exceptional;
6 |
7 | namespace Samples.AspNetCore.Controllers
8 | {
9 | public class HomeController : Controller
10 | {
11 | public IActionResult Index() => View();
12 |
13 | [HttpGet]
14 | public IActionResult Stacks() => View();
15 |
16 | [HttpPost]
17 | public IActionResult Stacks(IFormCollection form)
18 | {
19 | var error = new Exception("").Log(ControllerContext.HttpContext);
20 | error.Detail = form["details"];
21 | return Redirect("/Home/Exceptions/info?guid=" + error.GUID);
22 | }
23 |
24 | ///
25 | /// This lets you access the error handler via a route in your application, secured by whatever
26 | /// mechanisms are already in place.
27 | ///
28 | /// If mapping via RouteAttribute: [Route("errors/{path?}/{subPath?}")]
29 | public async Task Exceptions() => await ExceptionalMiddleware.HandleRequestAsync(HttpContext).ConfigureAwait(false);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Controllers/TestController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using StackExchange.Exceptional;
3 | using System;
4 | using System.Threading.Tasks;
5 |
6 | namespace Samples.AspNetCore.Controllers
7 | {
8 | public class TestController : Controller
9 | {
10 | public async Task Throw()
11 | {
12 | await ExceptionalUtils.Test.GetRedisException().LogAsync(ControllerContext.HttpContext).ConfigureAwait(false);
13 | await new Exception("").LogAsync(ControllerContext.HttpContext).ConfigureAwait(false);
14 |
15 | var ex = new Exception("This is an exception thrown from the Samples project! - Check out the log to see this exception.");
16 | // here's how your catch/throw might can add more info, for example SQL is special cased and shown in the UI:
17 | ex.Data["SQL"] = "Select * From FUBAR -- This is a SQL command!";
18 | ex.Data["Redis-Server"] = "REDIS01";
19 | ex.Data["Not-Included"] = "This key is skipped, because it's not in the web.config pattern";
20 | ex.AddLogData("Via Extension", "Some logged data via the .AddLoggedData() method!");
21 | throw ex;
22 | }
23 |
24 | public ActionResult ThrowRedis() => throw ExceptionalUtils.Test.GetRedisException();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace Samples.AspNetCore
5 | {
6 | public static class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | BuildWebHost(args).Run();
11 | }
12 |
13 | public static IWebHost BuildWebHost(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup()
16 | .Build();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Samples.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | true
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 | using StackExchange.Exceptional;
8 |
9 | namespace Samples.AspNetCore
10 | {
11 | public class Startup
12 | {
13 | public Startup(IConfiguration configuration, IWebHostEnvironment env)
14 | {
15 | Configuration = configuration;
16 | HostingEnvironment = env;
17 | }
18 |
19 | public IConfiguration Configuration { get; }
20 | public IWebHostEnvironment HostingEnvironment { get; }
21 |
22 | // This method gets called by the runtime. Use this method to add services to the container.
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.AddMvc();
26 | // Make IOptions available for injection everywhere
27 | services.AddExceptional(Configuration.GetSection("Exceptional"), settings =>
28 | {
29 | //settings.DefaultStore.ApplicationName = "Samples.AspNetCore";
30 | settings.UseExceptionalPageOnThrow = HostingEnvironment.IsDevelopment();
31 | });
32 | }
33 |
34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
35 | public void Configure(IApplicationBuilder app)
36 | {
37 | new Exception("Startup test exception - see how I'm captured! This happens due to a pre-.Configure() IStartupFilter").LogNoContext();
38 | // Boilerplate we're no longer using with Exceptional
39 | //if (env.IsDevelopment())
40 | //{
41 | // app.UseDeveloperExceptionPage();
42 | // app.UseBrowserLink();
43 | //}
44 | //else
45 | //{
46 | // app.UseExceptionHandler("/Home/Error");
47 | //}
48 | app.UseExceptional();
49 | app.UseStaticFiles();
50 |
51 | app.UseRouting();
52 | app.UseEndpoints(endpoints => endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Views/Home/Stacks.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Stack Test";
3 | }
4 |
5 |
6 |
7 |
8 |
Throw an exception with the below stack (for highlighting testing).
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
61 |
67 |
68 |
69 |
70 | @RenderSection("Scripts", required: false)
71 |
72 |
73 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Samples.AspNetCore
2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
3 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "_Layout";
3 | }
4 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "System": "Information",
7 | "Microsoft": "Information"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "asp.net",
3 | "private": true,
4 | "dependencies": {
5 | "bootstrap": "3.3.7",
6 | "jquery": "2.2.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/bundleconfig.json:
--------------------------------------------------------------------------------
1 | // Configure bundling and minification for the project.
2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241
3 | [
4 | {
5 | "outputFileName": "wwwroot/css/site.min.css",
6 | // An array of relative input file paths. Globbing patterns supported
7 | "inputFiles": [
8 | "wwwroot/css/site.css"
9 | ]
10 | },
11 | {
12 | "outputFileName": "wwwroot/js/site.min.js",
13 | "inputFiles": [
14 | "wwwroot/js/site.js"
15 | ],
16 | // Optionally specify minification options
17 | "minify": {
18 | "enabled": true,
19 | "renameLocals": true
20 | },
21 | // Optionally generate .map file
22 | "sourceMap": false
23 | }
24 | ]
25 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | /* Wrapping element */
7 | /* Set some basic padding to keep content from hitting the edges */
8 | .body-content {
9 | padding-left: 15px;
10 | padding-right: 15px;
11 | }
12 |
13 | /* Set widths on the form inputs since otherwise they're 100% wide */
14 | input,
15 | select {
16 | max-width: 280px;
17 | }
18 |
19 | /* Carousel */
20 | .carousel-caption p {
21 | font-size: 20px;
22 | line-height: 1.4;
23 | }
24 |
25 | /* Make .svg files in the carousel display properly in older browsers */
26 | .carousel-inner .item img[src$=".svg"] {
27 | width: 100%;
28 | }
29 |
30 | /* Hide/rearrange for smaller screens */
31 | @media screen and (max-width: 767px) {
32 | /* Hide captions */
33 | .carousel-caption {
34 | display: none;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/css/site.min.css:
--------------------------------------------------------------------------------
1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}@media screen and (max-width:767px){.carousel-caption{display:none}}
2 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/js/site.js:
--------------------------------------------------------------------------------
1 | // Write your JavaScript code.
2 |
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/js/site.min.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/js/site.min.js
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/samples/Samples.AspNetCore/wwwroot/lib/bootstrap/dist/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/samples/Samples.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
13 |
--------------------------------------------------------------------------------
/samples/Samples.Console/Errors/ExceptionsGoHere.txt:
--------------------------------------------------------------------------------
1 | This file exists for the sole purposes of demonstration, and so that the Errors folder we want to log to gets created.
--------------------------------------------------------------------------------
/samples/Samples.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using StackExchange.Exceptional;
3 | using StackExchange.Exceptional.Stores;
4 | using StackExchange.Exceptional.Notifiers;
5 | using static System.Console;
6 | using System.Threading.Tasks;
7 |
8 | namespace Samples.Console
9 | {
10 | public static class Program
11 | {
12 | private static void Main()
13 | {
14 | // Example of code-only setup, alternatively this can be in the App.config
15 | // RollupPeriod is null so a new file is always generated, for demonstration purposes
16 | Exceptional.Configure(settings =>
17 | {
18 | settings.DefaultStore = new JSONErrorStore(new ErrorStoreSettings
19 | {
20 | ApplicationName = "Samples.Console",
21 | Path = "Errors",
22 | RollupPeriod = null
23 | });
24 |
25 | // Example of a code-only email setup, alternatively this can be in the App.config
26 | settings.Register(new EmailNotifier(new EmailSettings
27 | {
28 | SMTPHost = "localhost", // Use Papercut here for testing: https://github.com/ChangemakerStudios/Papercut
29 | FromAddress = "exceptions@site.com",
30 | FromDisplayName = "Bob the Builder",
31 | ToAddress = "dont.use@thisadress.com"
32 | }));
33 | });
34 |
35 | // How to do it with normal roll-up
36 | //Exceptional.Configure(new ExceptionalSettings() { DefaultStore = new JSONErrorStore("Errors") });
37 |
38 | // Optional: for logging all unhandled exceptions
39 | Exceptional.ObserveAppDomainUnhandledExceptions();
40 |
41 | // Normally we wouldn't want to .GetAwaiter().GetResult(), but async Main is only on a the latest platforms at the moment
42 | DisplayExceptionStatsAsync().GetAwaiter().GetResult();
43 | PauseForInput();
44 |
45 | try
46 | {
47 | throw new Exception("Just a try/catch test");
48 | }
49 | catch (Exception ex)
50 | {
51 | ex.AddLogData("Example string", DateTime.UtcNow.ToString())
52 | .AddLogData("User Id", "You could fetch a user/account Id here, etc.")
53 | .AddLogData("Links get linkified", "https://www.google.com");
54 |
55 | // logged, but caught so we don't crash
56 | ex.LogNoContext();
57 | }
58 |
59 | DisplayExceptionStatsAsync().GetAwaiter().GetResult();
60 | PauseForInput();
61 |
62 | WriteLine("This next one will crash the program, but will be logged on the way out...");
63 | PauseForInput();
64 |
65 | // one not explicitly caught, will be logged by ExceptionHandler
66 | throw new Exception("I am an exception thrown on exit");
67 | }
68 |
69 | private static async Task DisplayExceptionStatsAsync()
70 | {
71 | var settings = Exceptional.Settings;
72 | WriteLine(settings.DefaultStore.Name + " for " + settings.DefaultStore.Name);
73 | var count = await settings.DefaultStore.GetCountAsync().ConfigureAwait(false);
74 | WriteLine("Exceptions in the log: " + count.ToString());
75 |
76 | var errors = await settings.DefaultStore.GetAllAsync().ConfigureAwait(false);
77 |
78 | if (errors.Count == 0) return;
79 |
80 | var last = errors[0];
81 | WriteLine($"Latest: {last.Message} on {last.CreationDate}");
82 | foreach (var customData in last.CustomData)
83 | WriteLine($" CustomData: '{customData.Key}': '{customData.Value}'");
84 | }
85 |
86 | private static void PauseForInput()
87 | {
88 | WriteLine("Press any key to continue...");
89 | ReadLine();
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/samples/Samples.Console/Samples.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | Samples.Console
5 | Exe
6 | bin\$(Configuration)\
7 | win7-x64
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/Samples.ConsoleNetCore/Errors/ExceptionsGoHere.txt:
--------------------------------------------------------------------------------
1 | This file exists for the sole purposes of demonstration, and so that the Errors folder we want to log to gets created.
--------------------------------------------------------------------------------
/samples/Samples.ConsoleNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System;
3 | using StackExchange.Exceptional;
4 | using StackExchange.Exceptional.Stores;
5 | using static System.Console;
6 | using System.Threading.Tasks;
7 |
8 | namespace Samples.NetCoreConsole
9 | {
10 | internal static class Program
11 | {
12 | private static async Task Main()
13 | {
14 | // Example of setting things up from appsettings.json config
15 | var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
16 | var exceptionalSettings = config.GetSection("Exceptional").Get();
17 | Exceptional.Configure(exceptionalSettings);
18 |
19 | // Example of code-only setup, alternatively this can be in the appsettings.json (or any config format) as shown above
20 | // RollupPeriod is null so a new file is always generated, for demonstration purposes
21 | //Exceptional.Configure(settings =>
22 | //{
23 | // settings.DefaultStore = new JSONErrorStore(new ErrorStoreSettings
24 | // {
25 | // ApplicationName = "Samples.ConsoleNetCore",
26 | // Path = "Errors",
27 | // RollupPeriod = null
28 | // });
29 | //});
30 |
31 | // How to do it with normal roll-up
32 | //Exceptional.Configure(new ExceptionalSettings() { DefaultStore = new JSONErrorStore("Errors") });
33 |
34 | // Optional: for logging all unhandled exceptions
35 | Exceptional.ObserveAppDomainUnhandledExceptions();
36 |
37 | await DisplayExceptionStatsAsync();
38 | PauseForInput();
39 |
40 | try
41 | {
42 | throw new Exception("Just a try/catch test");
43 | }
44 | catch (Exception ex)
45 | {
46 | ex.AddLogData("Example string", DateTime.UtcNow.ToString())
47 | .AddLogData("User Id", "You could fetch a user/account Id here, etc.")
48 | .AddLogData("Links get linkified", "https://www.google.com");
49 |
50 | // logged, but caught so we don't crash
51 | ex.LogNoContext();
52 | }
53 |
54 | await DisplayExceptionStatsAsync();
55 | PauseForInput();
56 |
57 | WriteLine("This next one will crash the program, but will be logged on the way out...");
58 | PauseForInput();
59 |
60 | // one not explicitly caught, will be logged by ExceptionHandler
61 | throw new Exception("I am an exception thrown on exit");
62 | }
63 |
64 | private static async Task DisplayExceptionStatsAsync()
65 | {
66 | var settings = Exceptional.Settings;
67 | WriteLine(settings.DefaultStore.Name + " for " + settings.DefaultStore.Name);
68 | var count = await settings.DefaultStore.GetCountAsync();
69 | WriteLine("Exceptions in the log: " + count.ToString());
70 |
71 | var errors = await settings.DefaultStore.GetAllAsync();
72 |
73 | if (errors.Count == 0) return;
74 |
75 | var last = errors[0];
76 | WriteLine($"Latest: {last.Message} on {last.CreationDate}");
77 | foreach (var customData in last.CustomData)
78 | WriteLine($" CustomData: '{customData.Key}': '{customData.Value}'");
79 | }
80 |
81 | private static void PauseForInput()
82 | {
83 | WriteLine("Press any key to continue...");
84 | ReadLine();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/samples/Samples.ConsoleNetCore/Samples.ConsoleNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/Samples.ConsoleNetCore/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | // This is a minimal illustration - see Samples.AspNetCore\appsetings.config for all examples
3 | "Exceptional": {
4 | "Store": {
5 | "ApplicationName": "Samples (.NET Core Console)",
6 | "Type": "JSON",
7 | "Path": "/Errors", //For file-based error stores. The path to use on for file storage.
8 | "Size": 200 //size defaults to 200, this is how many files are kept before the oldest error is removed
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/samples/Samples.MVC5/App_Start/BundleConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Optimization;
3 |
4 | namespace Samples.MVC5
5 | {
6 | public static class BundleConfig
7 | {
8 | // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
9 | public static void RegisterBundles(BundleCollection bundles)
10 | {
11 | bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
12 | MvcApplication.LogException(new Exception("Startup simulation: RegisterBundles Exception!"));
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/Samples.MVC5/App_Start/FilterConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Mvc;
3 |
4 | namespace Samples.MVC5
5 | {
6 | public static class FilterConfig
7 | {
8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters)
9 | {
10 | filters.Add(new HandleErrorAttribute());
11 | MvcApplication.LogException(new Exception("Startup simulation: RegisterGlobalFilters Exception!"));
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/samples/Samples.MVC5/App_Start/RouteConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Mvc;
3 | using System.Web.Routing;
4 |
5 | namespace Samples.MVC5
6 | {
7 | public static class RouteConfig
8 | {
9 | public static void RegisterRoutes(RouteCollection routes)
10 | {
11 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
12 |
13 | routes.MapRoute(
14 | name: "Default",
15 | url: "{controller}/{action}/{id}",
16 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
17 | );
18 |
19 | // map the /errors route for accessibility via a router, in addition to or instead of a handler directly
20 | routes.MapRoute(
21 | name: "Exceptions",
22 | url: "{controller}/{action}/{resource}/{subResource}",
23 | defaults:
24 | new
25 | {
26 | controller = "Home",
27 | action = "Exceptions",
28 | resource = UrlParameter.Optional,
29 | subResource = UrlParameter.Optional
30 | }
31 | );
32 | MvcApplication.LogException(new Exception("Startup simulation: RegisterRoutes Exception!"));
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Content/errors.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | $("footer").append("
Example: This text was added by a custom JavaScript include: errors.js
");
3 |
4 | // the Exception is exposed as a top level variable when on the detail page
5 | // all data present on the page is also available easily to JavaScript via this variable
6 | var ex = window.Exception;
7 | if (typeof (ex) === 'undefined') return;
8 |
9 | $('div.custom-data td.key:contains("User Id") + td').wrapInner(function() {
10 | return $('', { href: 'http://www.google.com/?q=' + encodeURIComponent(ex.CustomData["User Id"]), target: '_blank' });
11 | }).append(" < This wrapped in a link via the included errors.js");
12 | });
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using StackExchange.Exceptional;
2 | using System.Threading.Tasks;
3 | using System.Web.Mvc;
4 |
5 | namespace Samples.MVC5.Controllers
6 | {
7 | public class HomeController : Controller
8 | {
9 | public ActionResult Index(string validationTest)
10 | {
11 | ViewBag.Message = "This is a sample showing how to integrate Exceptional into your MVC4 application.";
12 |
13 | // For testing RequestValidationException, test something like: ?validationTest=
20 | /// This lets you access the error handler via a route in your application, secured by whatever
21 | /// mechanisms are already in place.
22 | ///
23 | /// If mapping via RouteAttribute: [Route("errors/{path?}/{subPath?}")]
24 | public Task Exceptions() => ExceptionalModule.HandleRequestAsync(System.Web.HttpContext.Current);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Global.asax:
--------------------------------------------------------------------------------
1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Samples.MVC5.MvcApplication" Language="C#" %>
2 |
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Global.asax.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Web;
4 | using System.Web.Mvc;
5 | using System.Web.Optimization;
6 | using System.Web.Routing;
7 | using StackExchange.Exceptional;
8 |
9 | namespace Samples.MVC5
10 | {
11 | // Note: For instructions on enabling IIS6 or IIS7 classic mode,
12 | // visit http://go.microsoft.com/?LinkId=9394801
13 |
14 | public class MvcApplication : HttpApplication
15 | {
16 | protected void Application_Start()
17 | {
18 | // Instead of any web.config entries, you can perform setup entirely through code
19 | // Setup Exceptional:
20 |
21 | // Memory example:
22 | //Exceptional.Configure(settings => settings.DefaultStore = new MemoryErrorStore());
23 | // JSON example
24 | //Exceptional.Configure(settings => settings.DefaultStore = new JSONErrorStore(path: "~/Errors"));
25 | // SQL Example
26 | //Exceptional.Configure(settings => settings.DefaultStore = new SQLErrorStore(applicationName: "My Error Log Name", connectionString: "Data Source=.;Initial Catalog=Exceptions;Integrated Security=SSPI;"));
27 |
28 | Exceptional.Configure(settings =>
29 | {
30 | // Optionally add custom data to any logged exception (visible on the exception detail page):
31 | settings.GetCustomData = (exception, data) =>
32 | {
33 | // exception is the exception thrown
34 | // data is a Dictionary to add custom data too
35 | data.Add("Example string", DateTime.UtcNow.ToString());
36 | data.Add("User Id", "You could fetch a user/account Id here, etc.");
37 | data.Add("Links get linkified", "https://www.google.com");
38 | };
39 | // Example of how to log command data for anything you want
40 | // These display the command and the data key/value pairs in the log
41 | settings.ExceptionActions.AddHandler((e, ex) =>
42 | {
43 | var cmd = e.AddCommand(new Command("Redis"));
44 | foreach (string k in ex.Data.Keys)
45 | {
46 | var val = ex.Data[k] as string;
47 | if (k == "redis-command") cmd.CommandString = val;
48 | if (k.StartsWith("Redis-")) cmd.AddData(k.Substring("Redis-".Length), val);
49 | }
50 | });
51 |
52 | settings.Render.JSIncludes.Add("/Content/errors.js");
53 | settings.OnBeforeLog += (sender, args) =>
54 | {
55 | args.Error.Message += " (suffix from OnBeforeLog handler)";
56 | //args.Abort = true; - you could stop the exception from being logged here
57 | };
58 | settings.OnAfterLog += (sender, args) =>
59 | {
60 | Trace.WriteLine("The logged exception GUID was: " + args.Error.GUID.ToString());
61 | // optionally var e = args.GetError() to fetch the actual error from the store
62 | };
63 | });
64 |
65 | AreaRegistration.RegisterAllAreas();
66 |
67 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
68 | RouteConfig.RegisterRoutes(RouteTable.Routes);
69 | BundleConfig.RegisterBundles(BundleTable.Bundles);
70 | }
71 |
72 | ///
73 | /// Example method to log an exception to the log...that' not shown to the user.
74 | ///
75 | /// The exception to log
76 | public static void LogException(Exception e)
77 | {
78 | // Note: When dealing with non-web applications, or logging from background threads,
79 | // you would pass, null in instead of a HttpContext object.
80 | e.Log(HttpContext.Current);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Images/accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.MVC5/Images/accent.png
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Images/bullet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.MVC5/Images/bullet.png
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Images/heroAccent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NickCraver/StackExchange.Exceptional/4fccb27bb8b1d9b66a5fc7c9b668d4625caedb21/samples/Samples.MVC5/Images/heroAccent.png
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:4000/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "Executable",
13 | "executablePath": "C:\\Program Files\\IIS Express\\iisexpress.exe",
14 | "commandLineArgs": "/path:\"$(SolutionDir)samples\\$(ProjectName)\" /port:4000",
15 | "launchBrowser": true,
16 | "launchUrl": "/"
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Samples.MVC5.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Samples.Mvc5
4 | net472
5 | Library
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/Samples.MVC5/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewBag.Title = "Home Page";
3 | }
4 | @section featured {
5 |
6 |
7 |
8 |
@ViewBag.Message
9 |
10 |
11 | Be sure to check out the project wikis for additional info.
12 |
13 |
14 |
15 | }
16 |
Sections to note about this project
17 |
18 | As an example refresh this page and check out the exceptions log above. You'll see some bundles being called that don't actually exist, causing exceptions.
19 |
20 |
Web.config entries (look at web.config for more details/options)
21 |
22 |
Section declaration (required, unless using code setup):
23 |
Route to access exception list (just map a route to this):
59 |
60 | public ActionResult Exceptions()
61 | {
62 | var context = System.Web.HttpContext.Current;
63 | var page = new HandlerFactory().GetHandler(context, Request.RequestType, Request.Url.ToString(), Request.PathInfo);
64 | page.ProcessRequest(context);
65 | return null;
66 | }
67 |
68 |
69 |
Static method to log exceptions (not shown to a user, straight to the log - this isn't needed at all, but can be used to hook up error logging for all exceptions via Application_OnError, or just logging events directly):
70 |
71 | ErrorStore.LogException(new Exception("This is an exception"), HttpContext.Current);
72 |
73 |
74 |
Include custom JS or CSS to include on error handler pages:
75 |