Implement real time statistic visualization (#16956)

Docs at https://trinitycore.atlassian.net/wiki/display/tc/Monitoring+a+TrinityCore+server

* Common/Graphs: Initial proof of concept

* Move influx db code to its own class

* Reuse the same socket

* Allow to log values of different categories

* Allow to log events

* Pass the timestamp to influxdb

* Send events in batches

* Send data async

* Log server shutdown.
Fix memory leak.

* Allow to enable/disable Stats in the settings and at runtime

* Read interval between each batch send from config

* Add InfluxDB connection info to configs

* Move each event category to its own table

* Log pathfinding queries

* Move categories table initialization to constructor using enum as key to avoid assigning the table name to the wrong enum value

* Log player login/logout events.
Pass the hostname correctly in the HTTP request.

* Fix linux build

* Handle "Connection: close" HTTP header, reconnecting on next scheduled send.
Disable StatsLogger if connection fails, logging the error.

* Add an enum for categories of logged values, it's still possible to pass a string instead of the enum.

* Don't log the whole batchedData when InfluxDB returns an error, it's too long and unreadable on console.

* Allow to call a function at a specified interval in thread-safe World::Update() context to log data like player count.

* Log map tile load/unload

* Core/StatsLogger: Allow logging more value types other than ints

https://docs.influxdata.com/influxdb/v0.10/write_protocols/write_syntax/

* Fix a typo in string escape of StatsLogger

* Yet more fixes to the escaping in FormatInfluxDBValue

* DB/Gameobject: Fix respawn time of few Quest GameObjects

By Tauriella, closes #16701

* DB/Misc: Fix some engrish

By tkrokli closes #16648

* Tools/MMaps: Add format library linking to mmaps_generator

(Very) partial cherry pick of ed75b0649a

* Core/StatsLogger: Simplify code

Convert values and categories arrays to maps initialized in-place
Remove constructor and destructor

* Core/StatsLogger: Add realm name to the event and value tags

* Log amount of processed packet of each session

* Apply recent singleton changes to sStatsLogger too

* Fix influxdb data format if no realm name is present

* Remove unneeded newlines from request body, fixes response 400 from InfluxDB 0.10

* Rename Reporting folder to Metric

* Rename StatsLogger to Metric

* Rename InfluxDB configs to Metric

* Add Grafana dashboards

* Add a random annoying macro

* Move string formatting to Metric::SendBatch(), reducing performance footprint of Metric::LogEvent() and Metric::LogValue()

* Update grafana graphs refresing tags on load and showing now-15m data, refreshing every minute. These settings can be modified in grafana.

* Rename MetricData fields

* Contrib/Grafana: Rename dashboard files

* Contrib/Grafana: Replace hardcoded Windows/Ubuntu realm names by the default, Trinity

* Config/Worldserver: Add missing section to the index

* Contrib/Grafana: Add singlestat panels with current online players, update diff averages (1 min, 5 mins and 15 mins)

http://i.imgur.com/Zi8lfvS.png

* Core/Metric: Replace the enums MetricEventCategory and MetricValueCategory by strings

For the sake of simplicity and less recompile time when adding new metrics, similar to how TC_LOG_* works

* Contrib/Grafana: Display the current number of online players and not its average

Closes #15075
(cherry picked from commit 3ae1016082)

# Conflicts:
#	src/server/game/Server/WorldSession.cpp
#	src/server/game/World/World.cpp
#	src/server/worldserver/Main.cpp
This commit is contained in:
DDuarte
2016-06-04 11:26:57 +01:00
parent 2c2bb4a237
commit 8b26aea95a
12 changed files with 1920 additions and 0 deletions

View File

@@ -0,0 +1,889 @@
{
"id": 1,
"title": "General info",
"originalTitle": "General info",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "25px",
"panels": [
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": null,
"editable": true,
"error": false,
"format": "none",
"id": 5,
"interval": null,
"isNew": true,
"links": [],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"span": 3,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"auto"
],
"type": "time"
}
],
"measurement": "online_players",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"type": "field",
"params": [
"value"
]
},
{
"type": "last",
"params": []
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/^$realm$/"
}
]
}
],
"thresholds": "",
"title": "Online players",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current",
"timeFrom": null,
"timeShift": null,
"hideTimeOverride": false
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": null,
"editable": true,
"error": false,
"format": "none",
"id": 6,
"interval": null,
"isNew": true,
"links": [],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"span": 3,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"auto"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "update_time_diff",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/^$realm$/"
}
]
}
],
"thresholds": "",
"title": "Update diff (avg)",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg",
"timeFrom": "1m"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": null,
"editable": true,
"error": false,
"format": "none",
"id": 7,
"interval": null,
"isNew": true,
"links": [],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"span": 3,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"auto"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "update_time_diff",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"thresholds": "",
"title": "Update diff (avg)",
"transparent": false,
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg",
"timeFrom": "5m"
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": null,
"editable": true,
"error": false,
"format": "none",
"id": 8,
"interval": null,
"isNew": true,
"links": [],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"span": 3,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"targets": [
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"auto"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "update_time_diff",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/^$realm$/"
}
]
}
],
"thresholds": "",
"title": "Update diff (avg)",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "avg",
"timeFrom": "15m",
"timeShift": null
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Update diff",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "update_time_diff",
"policy": "default",
"query": "SELECT mean(\"value\") FROM \"update_time_diff\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(null)",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/$realm$/"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "Update diff",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"xaxis": {
"show": true
},
"y-axis": true,
"y_formats": [
"ms",
"short"
],
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "Row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Online players",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "online_players",
"policy": "default",
"query": "SELECT mean(\"value\") FROM \"online_players\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(null)",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/$realm$/"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "Online players",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"xaxis": {
"show": true
},
"y-axis": true,
"y_formats": [
"short",
"short"
],
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "Logouts",
"transform": "negative-Y"
}
],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Logins",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "player_events",
"policy": "default",
"query": "SELECT count(\"text\") FROM \"player_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'Login' AND $timeFilter GROUP BY time($interval) fill(0)",
"rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"text"
],
"type": "field"
},
{
"params": [],
"type": "count"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=",
"value": "Trinity"
}
]
},
{
"alias": "Logouts",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"policy": "default",
"query": "SELECT count(\"text\") FROM \"player_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'Logout' AND $timeFilter GROUP BY time($interval) fill(0)",
"rawQuery": true,
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"timeFrom": null,
"timeShift": null,
"title": "Player login/logout",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"xaxis": {
"show": true
},
"y-axis": true,
"y_formats": [
"short",
"short"
],
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"title": "New row"
}
],
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": [
{
"allFormat": "regex values",
"current": {
"text": "Trinity",
"value": "Trinity"
},
"datasource": null,
"includeAll": false,
"multi": false,
"multiFormat": "regex values",
"name": "realm",
"options": [
{
"text": "Trinity",
"value": "Trinity",
"selected": true
}
],
"query": "show tag values from events with key = realm",
"refresh": 1,
"regex": "",
"type": "query"
}
]
},
"annotations": {
"list": [
{
"datasource": "Influx",
"enable": true,
"iconColor": "#C0C6BE",
"iconSize": 13,
"lineColor": "rgba(255, 96, 96, 0.592157)",
"name": "Global Events",
"query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
"showLine": true,
"textColumn": "text",
"titleColumn": "title"
}
]
},
"refresh": "1m",
"schemaVersion": 12,
"version": 7,
"links": []
}

339
contrib/grafana/2_Maps.json Normal file
View File

@@ -0,0 +1,339 @@
{
"id": 2,
"title": "Maps, vmaps and mmaps",
"originalTitle": "Maps, vmaps and mmaps",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 2,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "Unload tile",
"transform": "negative-Y"
}
],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Load tile",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"0"
],
"type": "fill"
}
],
"query": "SELECT count(\"title\") FROM \"map_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'LoadMapTile' AND $timeFilter GROUP BY time($interval) fill(0)",
"rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
},
{
"alias": "Unload tile",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"query": "SELECT count(\"title\") FROM \"map_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'UnloadMapTile' AND $timeFilter GROUP BY time($interval) fill(0)",
"rawQuery": true,
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"timeFrom": null,
"timeShift": null,
"title": "Map",
"tooltip": {
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
]
}
],
"title": "Row"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Pathfinding queries",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"query": "SELECT count(\"title\") FROM \"mmap_events\" WHERE \"realm\" =~ /$realm$/ AND \"title\" = 'CalculatePath' AND $timeFilter GROUP BY time($interval) fill(0)",
"rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": []
}
],
"timeFrom": null,
"timeShift": null,
"title": "MMap",
"tooltip": {
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
]
}
],
"title": "New row"
}
],
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": [
{
"allFormat": "regex values",
"current": {
"text": "Trinity",
"value": "Trinity"
},
"datasource": "Influx",
"includeAll": false,
"multi": false,
"multiFormat": "regex values",
"name": "realm",
"options": [
{
"text": "Trinity",
"value": "Trinity",
"selected": true
}
],
"query": "show tag values from events with key = realm",
"refresh": true,
"type": "query"
}
]
},
"annotations": {
"list": [
{
"datasource": "Influx",
"enable": true,
"iconColor": "#C0C6BE",
"iconSize": 13,
"lineColor": "rgba(255, 96, 96, 0.592157)",
"name": "Global Events",
"query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
"showLine": true,
"textColumn": "text",
"titleColumn": "title"
}
]
},
"refresh": "1m",
"schemaVersion": 8,
"version": 11,
"links": []
}

View File

@@ -0,0 +1,242 @@
{
"id": 3,
"title": "Network",
"originalTitle": "Network",
"tags": [],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": "Influx",
"editable": true,
"error": false,
"fill": 1,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"isNew": true,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "Processed packets",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"0"
],
"type": "fill"
}
],
"measurement": "processed_packets",
"query": "SELECT sum(\"value\") FROM \"processed_packets\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(0)",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "sum"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/$realm$/"
}
]
},
{
"alias": "Processed packets / mean per session",
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"0"
],
"type": "fill"
}
],
"measurement": "processed_packets",
"query": "SELECT mean(\"value\") FROM \"processed_packets\" WHERE \"realm\" =~ /$realm$/ AND $timeFilter GROUP BY time($interval) fill(0)",
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
}
]
],
"tags": [
{
"key": "realm",
"operator": "=~",
"value": "/$realm$/"
}
]
}
],
"timeFrom": null,
"timeShift": null,
"title": "Processed packets",
"tooltip": {
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
]
}
],
"title": "Row"
}
],
"time": {
"from": "now-15m",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": [
{
"allFormat": "regex values",
"current": {
"text": "Trinity",
"value": "Trinity"
},
"datasource": "Influx",
"includeAll": false,
"multi": false,
"multiFormat": "regex values",
"name": "realm",
"options": [
{
"text": "Trinity",
"value": "Trinity",
"selected": true
}
],
"query": "show tag values from events with key = realm",
"refresh": true,
"type": "query"
}
]
},
"annotations": {
"list": [
{
"datasource": "Influx",
"enable": true,
"iconColor": "#C0C6BE",
"iconSize": 13,
"lineColor": "rgba(255, 96, 96, 0.592157)",
"name": "Global Events",
"query": "select title, text from events where $timeFilter and realm =~ /$realm$/",
"showLine": true,
"textColumn": "text",
"titleColumn": "title"
}
]
},
"refresh": "1m",
"schemaVersion": 8,
"version": 7,
"links": []
}

View File

@@ -22,6 +22,7 @@
#include "VMapDefinitions.h"
#include "Log.h"
#include "Errors.h"
#include "Metric.h"
#include <string>
#include <sstream>
@@ -415,6 +416,8 @@ namespace VMAP
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
TC_METRIC_EVENT("map_events", "LoadMapTile",
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
return result;
}
@@ -473,6 +476,8 @@ namespace VMAP
}
}
iLoadedTiles.erase(tile);
TC_METRIC_EVENT("map_events", "UnloadMapTile",
"Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
}
void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Metric.h"
#include "Log.h"
#include "Config.h"
#include "Util.h"
void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger)
{
_realmName = realmName;
_batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
_overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
_overallStatusLogger = overallStatusLogger;
LoadFromConfigs();
}
bool Metric::Connect()
{
_dataStream.connect(_hostname, _port);
auto error = _dataStream.error();
if (error)
{
TC_LOG_ERROR("metric", "Error connecting to '%s:%s', disabling Metric. Error message : %s",
_hostname.c_str(), _port.c_str(), error.message().c_str());
_enabled = false;
return false;
}
_dataStream.clear();
return true;
}
void Metric::LoadFromConfigs()
{
bool previousValue = _enabled;
_enabled = sConfigMgr->GetBoolDefault("Metric.Enable", false);
_updateInterval = sConfigMgr->GetIntDefault("Metric.Interval", 10);
if (_updateInterval < 1)
{
TC_LOG_ERROR("metric", "'Metric.Interval' config set to %d, overriding to 1.", _updateInterval);
_updateInterval = 1;
}
_overallStatusTimerInterval = sConfigMgr->GetIntDefault("Metric.OverallStatusInterval", 1);
if (_overallStatusTimerInterval < 1)
{
TC_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to %d, overriding to 1.", _overallStatusTimerInterval);
_overallStatusTimerInterval = 1;
}
// Schedule a send at this point only if the config changed from Disabled to Enabled.
// Cancel any scheduled operation if the config changed from Enabled to Disabled.
if (_enabled && !previousValue)
{
std::string connectionInfo = sConfigMgr->GetStringDefault("Metric.ConnectionInfo", "");
if (connectionInfo.empty())
{
TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
return;
}
Tokenizer tokens(connectionInfo, ';');
if (tokens.size() != 3)
{
TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
return;
}
_hostname.assign(tokens[0]);
_port.assign(tokens[1]);
_databaseName.assign(tokens[2]);
Connect();
ScheduleSend();
ScheduleOverallStatusLog();
}
}
void Metric::Update()
{
if (_overallStatusTimerTriggered)
{
_overallStatusTimerTriggered = false;
_overallStatusLogger();
}
}
void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description)
{
using namespace std::chrono;
MetricData* data = new MetricData;
data->Category = category;
data->Timestamp = system_clock::now();
data->Type = METRIC_DATA_EVENT;
data->Title = title;
data->Text = description;
_queuedData.Enqueue(data);
}
void Metric::SendBatch()
{
using namespace std::chrono;
std::stringstream batchedData;
MetricData* data;
bool firstLoop = true;
while (_queuedData.Dequeue(data))
{
if (!firstLoop)
batchedData << "\n";
batchedData << data->Category;
if (!_realmName.empty())
batchedData << ",realm=" << _realmName;
batchedData << " ";
switch (data->Type)
{
case METRIC_DATA_VALUE:
batchedData << "value=" << data->Value;
break;
case METRIC_DATA_EVENT:
batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
break;
}
batchedData << " ";
batchedData << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
firstLoop = false;
delete data;
}
// Check if there's any data to send
if (batchedData.tellp() == std::streampos(0))
{
ScheduleSend();
return;
}
if (!_dataStream.good() && !Connect())
return;
_dataStream << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
_dataStream << "Host: " << _hostname << ":" << _port << "\r\n";
_dataStream << "Accept: */*\r\n";
_dataStream << "Content-Type: application/octet-stream\r\n";
_dataStream << "Content-Transfer-Encoding: binary\r\n";
_dataStream << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
_dataStream << batchedData.rdbuf();
std::string http_version;
_dataStream >> http_version;
unsigned int status_code = 0;
_dataStream >> status_code;
if (status_code != 204)
{
TC_LOG_ERROR("metric", "Error sending data, returned HTTP code: %u", status_code);
}
// Read and ignore the status description
std::string status_description;
std::getline(_dataStream, status_description);
// Read headers
std::string header;
while (std::getline(_dataStream, header) && header != "\r")
{
if (header == "Connection: close\r")
_dataStream.close();
}
ScheduleSend();
}
void Metric::ScheduleSend()
{
if (_enabled)
{
_batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
_batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
}
else
{
_dataStream.close();
MetricData* data;
// Clear the queue
while (_queuedData.Dequeue(data))
;
}
}
void Metric::ForceSend()
{
// Send what's queued only if io_service is stopped (so only on shutdown)
if (_enabled && _batchTimer->get_io_service().stopped())
SendBatch();
}
void Metric::ScheduleOverallStatusLog()
{
if (_enabled)
{
_overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
_overallStatusTimer->async_wait([this](const boost::system::error_code&)
{
_overallStatusTimerTriggered = true;
ScheduleOverallStatusLog();
});
}
}
Metric* Metric::instance()
{
static Metric instance;
return &instance;
}

141
src/common/Metric/Metric.h Normal file
View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef METRIC_H__
#define METRIC_H__
#include "Common.h"
#include "Threading/MPSCQueue.h"
#include <boost/asio/ip/tcp.hpp>
#include <boost/algorithm/string.hpp>
#include <type_traits>
enum MetricDataType
{
METRIC_DATA_VALUE,
METRIC_DATA_EVENT
};
struct MetricData
{
std::string Category;
std::chrono::time_point<std::chrono::system_clock> Timestamp;
MetricDataType Type;
// LogValue-specific fields
std::string Value;
// LogEvent-specific fields
std::string Title;
std::string Text;
};
class TC_COMMON_API Metric
{
private:
boost::asio::ip::tcp::iostream _dataStream;
MPSCQueue<MetricData> _queuedData;
std::unique_ptr<boost::asio::deadline_timer> _batchTimer;
std::unique_ptr<boost::asio::deadline_timer> _overallStatusTimer;
int32 _updateInterval = 0;
int32 _overallStatusTimerInterval = 0;
bool _enabled = false;
bool _overallStatusTimerTriggered = false;
std::string _hostname;
std::string _port;
std::string _databaseName;
std::function<void()> _overallStatusLogger;
std::string _realmName;
bool Connect();
void SendBatch();
void ScheduleSend();
void ScheduleOverallStatusLog();
template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
static std::string FormatInfluxDBValue(T value) { return std::to_string(value) + 'i'; }
static std::string FormatInfluxDBValue(std::string const& value)
{
return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
}
static std::string FormatInfluxDBValue(bool value) { return value ? "t" : "f"; }
static std::string FormatInfluxDBValue(const char* value) { return FormatInfluxDBValue(std::string(value)); }
static std::string FormatInfluxDBValue(double value) { return std::to_string(value); }
static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); }
public:
static Metric* instance();
void Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger = [](){});
void LoadFromConfigs();
void Update();
template<class T>
void LogValue(std::string const& category, T value)
{
using namespace std::chrono;
MetricData* data = new MetricData;
data->Category = category;
data->Timestamp = system_clock::now();
data->Type = METRIC_DATA_VALUE;
data->Value = FormatInfluxDBValue(value);
_queuedData.Enqueue(data);
}
void LogEvent(std::string const& category, std::string const& title, std::string const& description);
void ForceSend();
bool IsEnabled() const { return _enabled; }
};
#define sMetric Metric::instance()
#if PLATFORM != PLATFORM_WINDOWS
#define TC_METRIC_EVENT(category, title, description) \
do { \
if (sMetric->IsEnabled()) \
sMetric->LogEvent(category, title, description); \
} while (0)
#define TC_METRIC_VALUE(category, value) \
do { \
if (sMetric->IsEnabled()) \
sMetric->LogValue(category, value); \
} while (0)
#else
#define TC_METRIC_EVENT(category, title, description) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
do { \
if (sMetric->IsEnabled()) \
sMetric->LogEvent(category, title, description); \
} while (0) \
__pragma(warning(pop))
#define TC_METRIC_VALUE(category, value) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
do { \
if (sMetric->IsEnabled()) \
sMetric->LogValue(category, value); \
} while (0) \
__pragma(warning(pop))
#endif
#endif // METRIC_H__

View File

@@ -54,6 +54,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Metric.h"
class LoginQueryHolder : public SQLQueryHolder
{
@@ -1147,6 +1148,8 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
sScriptMgr->OnPlayerLogin(pCurrChar, firstLogin);
TC_METRIC_EVENT("player_events", "Login", pCurrChar->GetName());
delete holder;
}

View File

@@ -26,6 +26,7 @@
#include "DisableMgr.h"
#include "DetourCommon.h"
#include "DetourNavMeshQuery.h"
#include "Metric.h"
////////////////// PathGenerator //////////////////
PathGenerator::PathGenerator(const Unit* owner) :
@@ -62,6 +63,8 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo
if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
return false;
TC_METRIC_EVENT("mmap_events", "CalculatePath", "");
G3D::Vector3 dest(destX, destY, destZ);
SetEndPosition(dest);

View File

@@ -51,6 +51,7 @@
#include "BattlePetMgr.h"
#include "PacketUtilities.h"
#include "CollectionMgr.h"
#include "Metric.h"
#include <zlib.h>
@@ -456,6 +457,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
break;
}
TC_METRIC_VALUE("processed_packets", processedPackets);
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (m_Socket[CONNECTION_TYPE_REALM] && m_Socket[CONNECTION_TYPE_REALM]->IsOpen() && _warden)
@@ -614,6 +617,8 @@ void WorldSession::LogoutPlayer(bool save)
//! Call script hook before deletion
sScriptMgr->OnPlayerLogout(_player);
TC_METRIC_EVENT("player_events", "Logout", _player->GetName());
//! Remove the player from the world
// the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map

View File

@@ -63,6 +63,7 @@
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
#include "Metric.h"
#include "SupportMgr.h"
#include "TaxiPathGraph.h"
#include "TransportMgr.h"
@@ -437,6 +438,7 @@ void World::LoadConfigSettings(bool reload)
return;
}
sLog->LoadFromConfig();
sMetric->LoadFromConfigs();
}
m_defaultDbcLocale = LocaleConstant(sConfigMgr->GetIntDefault("DBC.Locale", 0));
@@ -2128,6 +2130,8 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.worldserver", "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000));
TC_METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds");
if (uint32 realmId = sConfigMgr->GetIntDefault("RealmID", 0)) // 0 reserved for auth
sLog->SetRealmId(realmId);
}
@@ -2430,6 +2434,10 @@ void World::Update(uint32 diff)
ProcessCliCommands();
sScriptMgr->OnWorldUpdate(diff);
// Stats logger update
sMetric->Update();
TC_METRIC_VALUE("update_time_diff", diff);
}
void World::ForceGameEventUpdate()

View File

@@ -45,6 +45,7 @@
#include "RealmList.h"
#include "DatabaseLoader.h"
#include "AppenderDB.h"
#include "Metric.h"
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include <boost/asio/io_service.hpp>
@@ -200,6 +201,13 @@ extern int main(int argc, char** argv)
LoadRealmInfo();
sMetric->Initialize(realm.Name, _ioService, []()
{
TC_METRIC_VALUE("online_players", sWorld->GetPlayerCount());
});
TC_METRIC_EVENT("events", "Worldserver started", "");
// Initialize the World
sScriptMgr->SetScriptLoader(AddScripts);
sWorld->SetInitialWorldSettings();
@@ -300,6 +308,9 @@ extern int main(int argc, char** argv)
StopDB();
TC_METRIC_EVENT("events", "Worldserver shutdown", "");
sMetric->ForceSend();
TC_LOG_INFO("server.worldserver", "Halting process...");
ShutdownCLIThread(cliThread);

View File

@@ -37,6 +37,7 @@
# LOGGING SYSTEM SETTINGS
# CURRENCIES SETTINGS
# PACKET SPOOF PROTECTION SETTINGS
# METRIC SETTINGS
#
###################################################################################################
@@ -3853,3 +3854,41 @@ PacketSpoof.BanDuration = 86400
#
###################################################################################################
###################################################################################################
# METRIC SETTINGS
#
# These settings control the statistics sent to the metric database (currently InfluxDB)
#
# Metric.Enable
# Description: Enables statistics sent to the metric database.
# Default: 0 - (Disabled)
# 1 - (Enabled)
Metric.Enable = 0
#
# Metric.Interval
# Description: Interval between every batch of data sent in seconds
# Default: 10 seconds
#
Metric.Interval = 10
#
# Metric.ConnectionInfo
# Description: Connection settings for metric database (currently InfluxDB).
# Example: "hostname;port;database"
# Default: "127.0.0.1;8086;worldserver"
Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
#
# Metric.OverallStatusInterval
# Description: Interval between every gathering of overall worldserver status data in seconds
# Default: 1 second
#
Metric.OverallStatusInterval = 1
###################################################################################################