David Cramer 7 лет назад
Родитель
Сommit
8300f5e67e

+ 2 - 1
bin/load-mocks

@@ -20,7 +20,7 @@ from pytz import utc
 from sentry import buffer, roles
 from sentry.models import (
     Activity, Broadcast, Commit, CommitAuthor, CommitFileChange, Deploy,
-    Environment, EventUser, File, GroupMeta, GroupRelease, Organization,
+    Environment, File, GroupMeta, GroupRelease, Organization,
     OrganizationAccessRequest, OrganizationMember, Project, Release,
     ReleaseCommit, ReleaseEnvironment, ReleaseFile, Repository, Team, User,
     UserReport
@@ -165,6 +165,7 @@ def create_sample_time_series(event, release=None):
         ), now, count)
         tsdb.incr_multi((
             (tsdb.models.organization_total_received, project.organization_id),
+            (tsdb.models.project_total_forwarded, project.id),
             (tsdb.models.project_total_received, project.id),
         ), now, int(count * 1.1))
         tsdb.incr_multi((

+ 2 - 0
src/sentry/api/endpoints/project_stats.py

@@ -59,6 +59,8 @@ class ProjectStatsEndpoint(ProjectEndpoint, StatsMixin):
             stat_model = tsdb.models.project_total_blacklisted
         elif stat == 'generated':
             stat_model = tsdb.models.project
+        elif stat == 'forwarded':
+            stat_model = tsdb.models.project_total_forwarded
         else:
             raise ValueError('Invalid stat: %s' % stat)
 

+ 4 - 3
src/sentry/plugins/bases/data_forwarding.py

@@ -1,7 +1,7 @@
 from __future__ import absolute_import
 
+from sentry import tsdb, ratelimits
 from sentry.api.serializers import serialize
-from sentry.app import ratelimiter
 from sentry.plugins.base import Plugin
 from sentry.plugins.base.configuration import react_plugin_config
 from sentry.plugins.status import PluginStatus
@@ -36,8 +36,9 @@ class DataForwardingPlugin(Plugin):
         )
         # limit segment to 50 requests/second
         limit, window = self.get_rate_limit()
-        if limit and window and ratelimiter.is_limited(rl_key, limit=limit, window=window):
+        if limit and window and ratelimits.is_limited(rl_key, limit=limit, window=window):
             return
 
         payload = self.get_event_payload(event)
-        return self.forward_event(event, payload)
+        self.forward_event(event, payload)
+        tsdb.incr(tsdb.models.project_total_forwarded, [event.project.id])

+ 85 - 0
src/sentry/static/sentry/app/views/projectDataForwarding.jsx

@@ -5,8 +5,91 @@ import LoadingError from '../components/loadingError';
 import LoadingIndicator from '../components/loadingIndicator';
 import PluginList from '../components/pluginList';
 import ProjectState from '../mixins/projectState';
+import StackedBarChart from '../components/stackedBarChart';
 import {t} from '../locale';
 
+const DataForwardingStats = React.createClass({
+  mixins: [ApiMixin],
+
+  getInitialState() {
+    let until = Math.floor(new Date().getTime() / 1000);
+    let since = until - 3600 * 24 * 30;
+
+    return {
+      since: since,
+      until: until,
+      loading: true,
+      error: false,
+      stats: null,
+      emptyStats: false
+    };
+  },
+
+  componentWillMount() {
+    this.fetchData();
+  },
+
+  fetchData() {
+    let {orgId, projectId} = this.props.params;
+    this.api.request(`/projects/${orgId}/${projectId}/stats/`, {
+      query: {
+        since: this.state.since,
+        until: this.state.until,
+        resolution: '1d',
+        stat: 'forwarded'
+      },
+      success: data => {
+        let emptyStats = true;
+        let stats = data.map(p => {
+          if (p[0]) emptyStats = false;
+          return {x: p[0], y: [p[1]]};
+        });
+        this.setState({
+          stats: stats,
+          emptyStats: emptyStats,
+          error: false,
+          loading: false
+        });
+      },
+      error: () => {
+        this.setState({error: true, loading: false});
+      }
+    });
+  },
+
+  render() {
+    if (this.state.loading) return <div className="box"><LoadingIndicator /></div>;
+    else if (this.state.error) return <LoadingError onRetry={this.fetchData} />;
+
+    return (
+      <div className="panel panel-default">
+        <div className="panel-heading">
+          <h6>{t('Forwarded events in the last 30 days (by day)')}</h6>
+        </div>
+        <div className="panel-body p-a-0">
+          {!this.state.emptyStats
+            ? <div className="inbound-filters-stats p-a-1">
+                <div className="bar-chart">
+                  <StackedBarChart
+                    points={this.state.stats}
+                    height={50}
+                    barClasses={['accepted']}
+                    className="sparkline m-b-0"
+                  />
+                </div>
+              </div>
+            : <div className="blankslate p-y-2">
+                <h5>{t('Nothing forwarded in the last 30 days.')}</h5>
+                <p className="m-b-0">
+                  {t('Total events forwarded to third party integrations.')}
+                </p>
+              </div>}
+        </div>
+      </div>
+    );
+  }
+});
+
 export default React.createClass({
   mixins: [ApiMixin, ProjectState],
 
@@ -101,6 +184,7 @@ export default React.createClass({
   },
 
   render() {
+    let {params} = this.props;
     return (
       <div>
         <h1>{t('Data Forwarding')}</h1>
@@ -122,6 +206,7 @@ export default React.createClass({
             </p>
           </div>
         </div>
+        <DataForwardingStats params={params} />
         {this.renderBody()}
       </div>
     );

+ 2 - 0
src/sentry/tsdb/base.py

@@ -45,6 +45,8 @@ class TSDBModel(Enum):
     project_operation_errors = 103
     # the number of events blocked due to being blacklisted
     project_total_blacklisted = 104
+    # the number of events forwarded to third party processors (data forwarding)
+    project_total_forwarded = 105
 
     # the number of events sent to the server
     organization_total_received = 200