You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+89-24Lines changed: 89 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -84,7 +84,7 @@ var log = new LoggerConfiguration()
84
84
85
85
## Table definition
86
86
87
-
You'll need to create a table like this in your database:
87
+
You'll need to create a table like this in your database. Many other variations are possible. In particular, give careful consideration to whether you need the Id column (discussed in the next section). The table definition shown here is the default configuration.
88
88
89
89
```
90
90
CREATE TABLE [Logs] (
@@ -95,8 +95,7 @@ CREATE TABLE [Logs] (
95
95
[Level] nvarchar(128) NULL,
96
96
[TimeStamp] datetimeoffset(7) NOT NULL, -- use datetime for SQL Server pre-2008
97
97
[Exception] nvarchar(max) NULL,
98
-
[Properties] xml NULL,
99
-
[LogEvent] nvarchar(max) NULL
98
+
[Properties] xml NULL
100
99
101
100
CONSTRAINT [PK_Logs]
102
101
PRIMARY KEY CLUSTERED ([Id] ASC)
@@ -107,23 +106,83 @@ CREATE TABLE [Logs] (
107
106
) ON [PRIMARY];
108
107
```
109
108
110
-
**Remember to grant the necessary permissions for the sink to be able to write to the log table.**
109
+
If you don't plan to use a column, you can specify which columns to exclude in the `columnOptions.Store` parameter (see below).
111
110
112
-
If you don't plan on using one or more columns, you can specify which columns to include in the *columnOptions.Store* parameter (see below).
113
-
114
-
The Level column should be defined as a TinyInt if the *columnOptions.Level.StoreAsEnum* is set to true.
111
+
The Level column should be defined as a `tinyint` when `columnOptions.Level.StoreAsEnum` is set to `true`.
115
112
116
113
117
114
### Automatic table creation
118
115
119
-
If you set the `autoCreateSqlTable` option to `true`, the sink will create a table for you in the database specified in the connection string. Make sure that the user associated with this connection string has enough rights to make schema changes.
116
+
If you set the `autoCreateSqlTable` option to `true`, the sink will create a table for you in the database specified in the connection string. Make sure that the user associated with this connection string has enough rights to make schema changes; see below.
117
+
118
+
119
+
### Permissions
120
+
121
+
At a minimum, writing log entries requires SELECT and INSERT permissions for the log table. (SELECT is required because the sink's batching behavior uses bulk inserts which reads the schema before the write operations begin).
122
+
123
+
SQL permissions are a very complex subject. Here is an example of one possible solution (valid for SQL 2012 or later):
124
+
125
+
```
126
+
CREATE ROLE [SerilogAutoCreate];
127
+
GRANT SELECT ON sys.tables TO [SerilogAutoCreate];
128
+
GRANT SELECT ON sys.schemas TO [SerilogAutoCreate];
129
+
GRANT ALTER ON SCHEMA::[dbo] TO [SerilogAutoCreate]
130
+
GRANT CREATE TABLE ON DATABASE::[SerilogTest] TO [SerilogAutoCreate];
131
+
132
+
CREATE ROLE [SerilogWriter];
133
+
GRANT SELECT TO [SerilogWriter];
134
+
GRANT INSERT TO [SerilogWriter];
135
+
136
+
CREATE LOGIN [Serilog] WITH PASSWORD = 'password';
137
+
138
+
CREATE USER [Serilog] FOR LOGIN [Serilog] WITH DEFAULT_SCHEMA = dbo;
139
+
GRANT CONNECT TO [Serilog];
140
+
141
+
ALTER ROLE [SerilogAutoCreate] ADD MEMBER [Serilog];
142
+
ALTER ROLE [SerilogWriter] ADD MEMBER [Serilog];
143
+
```
144
+
145
+
This creates a SQL login named `Serilog`, a database user named `Serilog`, and assigned to that user are the roles `SerilogAutoCreate` and `SerilogWriter`. As the name suggests, the SerilogAutoCreate role is not needed if you create the database ahead of time, which is the recommended course of action if you're concerned about security at this level.
146
+
147
+
Also, ideally the SerilogWriter role would be restricted to the log table only, and that table has to already exist for table-specific GRANT statements to execute, so that's another reason that you probably don't want to use auto-create. Table-level restrictions would look like this (assuming you name your log table SecuredLog, of course):
148
+
149
+
```
150
+
GRANT SELECT ON [dbo].[SecuredLog] TO [SerilogWriter];
151
+
GRANT SELECT ON [dbo].[SecuredLog] TO [SerilogWriter];
152
+
```
153
+
154
+
There are many possible variations. For example, you could also create a new schema that was specific to the log(s) and restrict access that way.
155
+
156
+
157
+
## Id Column Options
158
+
159
+
Previous versions of this sink assumed the Id column is always present as an `int``IDENTITY` primary key with a clustered index. Other configurations are available, however this is still the default for backwards-compatibility reasons.
160
+
161
+
You should consider your anticipated logging volume and query requirements carefully. The default setting is not especially useful in real-world query scenarios since a clustered index is primarily of use when the key is used for sorting or range searches, which will rarely be the case for the Id column.
162
+
163
+
### No Id Column
164
+
165
+
If you eliminate the Id column completely, the log table is stored as an unindexed heap. This is the ideal write-speed scenario for logging, however any non-clustered indexes you add will slightly degrade write performance. One way to mitigate this is to keep the non-clustered indexes offline and use batch reindexing on a scheduled basis. If you create your table ahead of time, simply omit the Id column and the constraint shown in the previous section.
166
+
167
+
### Unclustered Id Column
168
+
169
+
You can also retain the Id column as an `IDENTITY` primary key, but using a non-clustered index. The log is still stored as an unindexed heap, but writes with non-clustered indexes are slightly faster. Non-clustered indexes on other columns will reference the Id primary key. However, read performance will be slightly degraded since it requires two reads (the covering non-clustered index, then dereferencing the heap row from the Id). To create this type of table ahead of time, change the constraint in the previous section to `NONCLUSTERED` and leave out the `WITH` clause.
120
170
171
+
### Bigint Data Type
172
+
173
+
For very large log tables, you may wish to create the Id column with the `bigint` datatype. This 8-byte integer will permit a maximum identity value of 9,223,372,036,854,775,807. The only change to the table syntax in the previous section is the datatype where `[Id]` is defined. This will slightly degrade both read and write performance.
174
+
175
+
## Batch Size and Performance
176
+
177
+
This is a "periodic batching sink." This means the sink will queue a certain number of log events before they're actually written to SQL Server as a bulk insert operation. There is also a timeout so that the batch is always written even if it has not been filled. By default, the batch size is 50 and the timeout is 5 seconds. You can change these through configuration.
178
+
179
+
Consider increasing the batch size in high-volume logging environments. In one test of a loop writing a single log entry to a local server instance (no network traffic), the default batch achieved around 14,000 rows per second. Increasing the batch size to 1000 rows increased write speed to nearly 43,000 rows per second. However, you should also consider the risk-factor. If the server crashes or the connection goes down, you may lose an entire batch of log entries. You can mitigate this by reducing the timeout. Run performance tests to find the optimal batch size for your production log content, network setup, and server configuration.
121
180
122
181
## Standard columns
123
182
124
-
The "standard columns" used by this sink (apart from obvious required columns like Id) are described by the StandardColumn enumeration and controlled through code by the `columnOptions.Store` collection.
183
+
The "standard columns" used by this sink are described by the `StandardColumn` enumeration and controlled through code by the `columnOptions.Store` collection. By default (and consistent with the SQL command to create a table, above) these columns are included:
125
184
126
-
By default (and consistent with the SQL command to create a table, above) these columns are included:
You can also store your own log event properties as additional columns; see below.
203
+
You can also store your own log event properties in additional custom columns; see below.
145
204
146
-
147
-
### Saving properties in additional columns
205
+
### Saving properties in custom columns
148
206
149
207
By default any log event properties you include in your log statements will be saved to the Properties column (and/or LogEvent column, per columnOption.Store). But they can also be stored in their own columns via the AdditionalDataColumns setting.
150
208
@@ -153,25 +211,30 @@ var columnOptions = new ColumnOptions
The log event properties `User` and `Other` will now be placed in the corresponding column upon logging. The property name must match a column name in your table. Be sure to include them in the table definition.
224
+
The log event properties `UserName` and `RequestUri` will be written to the corresponding columns whenever those values (with the exact same property name) occur in a log entry. Be sure to include them in the table definition if you create your table ahead of time.
225
+
226
+
When configuring through code, set the `DataType` property to a .NET type. When configuring through XML, JSON or other settings packages, specify a SQL data type. It will be internally converted to an equivalent .NET type. Variable-length data types like `string` and `varchar` require a `DataLength` property. Use -1 to specify SQL's `MAX` length.
167
227
228
+
**Standard column names are reserved. Even if you exclude a standard column, never create a custom column by the same name.**
168
229
169
-
#### Excluding redundant items from the Properties column
230
+
231
+
#### Excluding redundant Properties or LogEvent data
170
232
171
233
By default, additional properties will still be included in the data saved to the XML Properties or JSON LogEvent column (assuming one or both are enabled via the `columnOptions.Store` parameter). This is consistent with the idea behind structured logging, and makes it easier to convert the log data to another (e.g. NoSQL) storage platform later if desired.
172
234
173
-
However, if necessary, then the properties being saved in their own columns can be excluded from the data. Use the `columnOptions.Properties.ExcludeAdditionalProperties` parameter in the sink configuration to exclude the redundant properties from the XML.
235
+
However, if necessary, the properties being saved in their own columns can be excluded from the data. Use the `columnOptions.Properties.ExcludeAdditionalProperties` parameter in the sink configuration to exclude the redundant properties from the XML, or `columnOptions.LogEvent.ExcludeAdditionalProperties` if you've added the JSON LogEvent column.
174
236
237
+
The standard columns are always excluded from the Properties and LogEvent columns.
175
238
176
239
### Columns defined by AppSettings (.NET Framework)
177
240
@@ -219,7 +282,7 @@ The equivalent of adding custom columns as shown in the .NET Framework example a
219
282
}
220
283
```
221
284
222
-
As the name suggests, `columnOptionSection` is an entire configuration section in its own right. All possible entries and some sample values are shown below. All properties and subsections are optional.
285
+
As the name suggests, `columnOptionSection` is an entire configuration section in its own right. All possible entries and some sample values are shown below. All properties and subsections are optional. It is not currently possible to specify a `PropertiesFilter` predicate in configuration.
223
286
224
287
```json
225
288
"columnOptionsSection": {
@@ -230,7 +293,7 @@ As the name suggests, `columnOptionSection` is an entire configuration section i
@@ -256,11 +319,13 @@ As the name suggests, `columnOptionSection` is an entire configuration section i
256
319
```
257
320
258
321
259
-
### Options for serialization of the log event data
322
+
### Options for serialization of event data
323
+
324
+
Typically you will choose either XML or JSON serialization, but not both.
260
325
261
326
#### JSON (LogEvent column)
262
327
263
-
The log event JSON can be stored to the LogEvent column. This can be enabled by adding the LogEvent column to the `columnOptions.Store` collection. Use the `columnOptions.LogEvent.ExcludeAdditionalProperties` parameter to exclude redundant properties from the JSON. This is analogue to excluding redundant items from XML in the Properties column.
328
+
Event data items can be stored to the LogEvent column. This can be enabled by adding the LogEvent column to the `columnOptions.Store` collection. Use the `columnOptions.LogEvent.ExcludeAdditionalProperties` parameter to exclude redundant properties from the JSON. This is analogue to excluding redundant items from XML in the Properties column.
264
329
265
330
#### XML (Properties column)
266
331
@@ -276,7 +341,7 @@ If `OmitDictionaryContainerElement`, `OmitSequenceContainerElement` or `OmitStru
276
341
277
342
If `OmitElementIfEmpty` is set then if a property is empty, it will not be serialized.
278
343
279
-
##### Querying the properties XML
344
+
##### Querying the Properties XML data
280
345
281
346
Extracting and querying the properties data directly can be helpful when looking for specific log sequences.
Copy file name to clipboardExpand all lines: src/Serilog.Sinks.MSSqlServer/Configuration/Microsoft.Extensions.Configuration/LoggerConfigurationMSSqlServerExtensions.cs
0 commit comments