load-mocks 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. #!/usr/bin/env python
  2. import time
  3. from sentry.runner import configure
  4. configure()
  5. import itertools
  6. import random
  7. from datetime import datetime, timedelta
  8. from hashlib import sha1
  9. from random import randint
  10. from uuid import uuid4
  11. from django.conf import settings
  12. from django.db import IntegrityError, transaction
  13. from django.db.models import F
  14. from django.utils import timezone
  15. from pytz import utc
  16. from sentry import buffer, roles, tsdb
  17. from sentry.event_manager import HashDiscarded
  18. from sentry.incidents.logic import (
  19. AlertRuleNameAlreadyUsedError,
  20. create_alert_rule,
  21. create_alert_rule_trigger,
  22. create_incident,
  23. )
  24. from sentry.incidents.models import AlertRuleThresholdType, IncidentType
  25. from sentry.models import (
  26. TOMBSTONE_FIELDS_FROM_GROUP,
  27. Activity,
  28. Broadcast,
  29. CheckInStatus,
  30. Commit,
  31. CommitAuthor,
  32. CommitFileChange,
  33. Deploy,
  34. Environment,
  35. EventAttachment,
  36. File,
  37. Group,
  38. GroupRelease,
  39. GroupTombstone,
  40. Monitor,
  41. MonitorCheckIn,
  42. MonitorStatus,
  43. MonitorType,
  44. Organization,
  45. OrganizationAccessRequest,
  46. OrganizationMember,
  47. Project,
  48. Release,
  49. ReleaseCommit,
  50. ReleaseEnvironment,
  51. ReleaseFile,
  52. ReleaseProjectEnvironment,
  53. Repository,
  54. Team,
  55. User,
  56. UserReport,
  57. )
  58. from sentry.signals import mocks_loaded
  59. from sentry.similarity import features
  60. from sentry.utils import loremipsum
  61. from sentry.utils.hashlib import md5_text
  62. from sentry.utils.samples import create_sample_event as _create_sample_event
  63. from sentry.utils.samples import create_trace, generate_user, random_normal
  64. PLATFORMS = itertools.cycle(["ruby", "php", "python", "java", "javascript"])
  65. LEVELS = itertools.cycle(["error", "error", "error", "fatal", "warning"])
  66. ENVIRONMENTS = itertools.cycle(["production", "production", "staging", "alpha", "beta", ""])
  67. MONITOR_NAMES = itertools.cycle(settings.CELERYBEAT_SCHEDULE.keys())
  68. MONITOR_SCHEDULES = itertools.cycle(["* * * * *", "0 * * * *", "0 0 * * *"])
  69. LONG_MESSAGE = """Code: 0.
  70. DB::Exception: String is too long for DateTime: 2018-10-26T19:14:18+00:00. Stack trace:
  71. 0. clickhouse-server(StackTrace::StackTrace()+0x16) [0x99e9626]
  72. 1. clickhouse-server(DB::Exception::Exception(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int)+0x22) [0x3087172]
  73. 2. clickhouse-server(DB::FunctionComparison<DB::EqualsOp, DB::NameEquals>::executeDateOrDateTimeOrEnumOrUUIDWithConstString(DB::Block&, unsigned long, DB::IColumn const*, DB::IColumn const*, std::shared_ptr<DB::IDataType const> const&, std::shared_ptr<DB::IDataType const> const&, bool, unsigned long)+0x13c8) [0x3b233d8]
  74. 3. clickhouse-server(DB::FunctionComparison<DB::EqualsOp, DB::NameEquals>::executeImpl(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long)+0x576) [0x3bafc86]
  75. 4. clickhouse-server(DB::PreparedFunctionImpl::defaultImplementationForNulls(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long)+0x174) [0x7953cd4]
  76. 5. clickhouse-server(DB::PreparedFunctionImpl::executeWithoutLowCardinalityColumns(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long)+0x54) [0x7953b04]
  77. 6. clickhouse-server(DB::PreparedFunctionImpl::execute(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long)+0x3e2) [0x7954222]
  78. 7. clickhouse-server(DB::ExpressionAction::execute(DB::Block&, std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unsigned long> > >&) const+0x69b) [0x7b021fb]
  79. 8. clickhouse-server(DB::ExpressionActions::execute(DB::Block&) const+0xe6) [0x7b03676]
  80. 9. clickhouse-server(DB::FilterBlockInputStream::FilterBlockInputStream(std::shared_ptr<DB::IBlockInputStream> const&, std::shared_ptr<DB::ExpressionActions> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)+0x711) [0x79b7e31]
  81. 10. clickhouse-server() [0x75e9443]
  82. 11. clickhouse-server(DB::InterpreterSelectQuery::executeImpl(DB::InterpreterSelectQuery::Pipeline&, std::shared_ptr<DB::IBlockInputStream> const&, bool)+0x118f) [0x75f212f]
  83. 12. clickhouse-server(DB::InterpreterSelectQuery::InterpreterSelectQuery(std::shared_ptr<DB::IAST> const&, DB::Context const&, std::shared_ptr<DB::IBlockInputStream> const&, std::shared_ptr<DB::IStorage> const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, DB::QueryProcessingStage::Enum, unsigned long, bool)+0x5e6) [0x75f2d46]
  84. 13. clickhouse-server(DB::InterpreterSelectQuery::InterpreterSelectQuery(std::shared_ptr<DB::IAST> const&, DB::Context const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, DB::QueryProcessingStage::Enum, unsigned long, bool)+0x56) [0x75f3aa6]
  85. 14. clickhouse-server(DB::InterpreterSelectWithUnionQuery::InterpreterSelectWithUnionQuery(std::shared_ptr<DB::IAST> const&, DB::Context const&, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, DB::QueryProcessingStage::Enum, unsigned long, bool)+0x7e7) [0x75ffab7]
  86. 15. clickhouse-server(DB::InterpreterFactory::get(std::shared_ptr<DB::IAST>&, DB::Context&, DB::QueryProcessingStage::Enum)+0x3a8) [0x75dc138]
  87. 16. clickhouse-server() [0x768fad9]
  88. 17. clickhouse-server(DB::executeQuery(std::__cxx11::basic..."""
  89. def make_sentence(words=None):
  90. if words is None:
  91. words = int(random.weibullvariate(8, 3))
  92. return " ".join(random.choice(loremipsum.words) for _ in range(words))
  93. def create_sample_event(*args, **kwargs):
  94. try:
  95. event = _create_sample_event(*args, **kwargs)
  96. except HashDiscarded as e:
  97. print(f"> Skipping Event: {e.message}") # NOQA
  98. else:
  99. if event is not None:
  100. features.record([event])
  101. return event
  102. def generate_commits(user):
  103. commits = []
  104. for i in range(random.randint(1, 20)):
  105. if i == 1:
  106. filename = "raven/base.py"
  107. else:
  108. filename = random.choice(loremipsum.words) + ".js"
  109. if random.randint(0, 5) == 1:
  110. author = (user.name, user.email)
  111. else:
  112. author = (
  113. f"{random.choice(loremipsum.words)} {random.choice(loremipsum.words)}",
  114. f"{random.choice(loremipsum.words)}@example.com",
  115. )
  116. commits.append(
  117. {
  118. "key": sha1(uuid4().bytes).hexdigest(),
  119. "message": f"feat: Do something to {filename}\n{make_sentence()}",
  120. "author": author,
  121. "files": [(filename, "M")],
  122. }
  123. )
  124. return commits
  125. def generate_tombstones(project, user):
  126. # attempt to create a high enough previous_group_id
  127. # that it won't conflict with any group ids
  128. prev_group_id = 100000
  129. try:
  130. prev_group_id = (
  131. max(
  132. GroupTombstone.objects.order_by("-previous_group_id")[0].previous_group_id,
  133. prev_group_id,
  134. )
  135. + 1
  136. )
  137. except IndexError:
  138. pass
  139. for group in Group.objects.filter(project=project)[:5]:
  140. GroupTombstone.objects.create(
  141. previous_group_id=prev_group_id,
  142. actor_id=user.id,
  143. **{name: getattr(group, name) for name in TOMBSTONE_FIELDS_FROM_GROUP},
  144. )
  145. prev_group_id += 1
  146. def create_system_time_series():
  147. now = datetime.utcnow().replace(tzinfo=utc)
  148. for _ in range(60):
  149. count = randint(1, 10)
  150. tsdb.incr_multi(
  151. (
  152. (tsdb.models.internal, "client-api.all-versions.responses.2xx"),
  153. (tsdb.models.internal, "client-api.all-versions.requests"),
  154. ),
  155. now,
  156. int(count * 0.9),
  157. )
  158. tsdb.incr_multi(
  159. ((tsdb.models.internal, "client-api.all-versions.responses.4xx"),),
  160. now,
  161. int(count * 0.05),
  162. )
  163. tsdb.incr_multi(
  164. ((tsdb.models.internal, "client-api.all-versions.responses.5xx"),),
  165. now,
  166. int(count * 0.1),
  167. )
  168. now = now - timedelta(seconds=1)
  169. for _ in range(24 * 30):
  170. count = randint(100, 1000)
  171. tsdb.incr_multi(
  172. (
  173. (tsdb.models.internal, "client-api.all-versions.responses.2xx"),
  174. (tsdb.models.internal, "client-api.all-versions.requests"),
  175. ),
  176. now,
  177. int(count * 4.9),
  178. )
  179. tsdb.incr_multi(
  180. ((tsdb.models.internal, "client-api.all-versions.responses.4xx"),),
  181. now,
  182. int(count * 0.05),
  183. )
  184. tsdb.incr_multi(
  185. ((tsdb.models.internal, "client-api.all-versions.responses.5xx"),),
  186. now,
  187. int(count * 0.1),
  188. )
  189. now = now - timedelta(hours=1)
  190. def create_sample_time_series(event, release=None):
  191. if event is None:
  192. return
  193. group = event.group
  194. project = group.project
  195. key = project.key_set.all()[0]
  196. now = datetime.utcnow().replace(tzinfo=utc)
  197. environment = Environment.get_or_create(
  198. project=project, name=Environment.get_name_or_default(event.get_tag("environment"))
  199. )
  200. if release:
  201. ReleaseEnvironment.get_or_create(
  202. project=project, release=release, environment=environment, datetime=now
  203. )
  204. grouprelease = GroupRelease.get_or_create(
  205. group=group, release=release, environment=environment, datetime=now
  206. )
  207. for _ in range(60):
  208. count = randint(1, 10)
  209. tsdb.incr_multi(
  210. ((tsdb.models.project, project.id), (tsdb.models.group, group.id)),
  211. now,
  212. count,
  213. environment_id=environment.id,
  214. )
  215. tsdb.incr_multi(
  216. (
  217. (tsdb.models.organization_total_received, project.organization_id),
  218. (tsdb.models.project_total_received, project.id),
  219. (tsdb.models.key_total_received, key.id),
  220. ),
  221. now,
  222. int(count * 1.1),
  223. )
  224. tsdb.incr(
  225. tsdb.models.project_total_forwarded,
  226. project.id,
  227. now,
  228. int(count * 1.1),
  229. )
  230. tsdb.incr_multi(
  231. (
  232. (tsdb.models.organization_total_rejected, project.organization_id),
  233. (tsdb.models.project_total_rejected, project.id),
  234. (tsdb.models.key_total_rejected, key.id),
  235. ),
  236. now,
  237. int(count * 0.1),
  238. )
  239. frequencies = [
  240. (tsdb.models.frequent_issues_by_project, {project.id: {group.id: count}}),
  241. (tsdb.models.frequent_environments_by_group, {group.id: {environment.id: count}}),
  242. ]
  243. if release:
  244. frequencies.append(
  245. (tsdb.models.frequent_releases_by_group, {group.id: {grouprelease.id: count}})
  246. )
  247. tsdb.record_frequency_multi(frequencies, now)
  248. now = now - timedelta(seconds=1)
  249. for _ in range(24 * 30):
  250. count = randint(100, 1000)
  251. tsdb.incr_multi(
  252. ((tsdb.models.project, group.project.id), (tsdb.models.group, group.id)),
  253. now,
  254. count,
  255. environment_id=environment.id,
  256. )
  257. tsdb.incr_multi(
  258. (
  259. (tsdb.models.organization_total_received, project.organization_id),
  260. (tsdb.models.project_total_received, project.id),
  261. (tsdb.models.key_total_received, key.id),
  262. ),
  263. now,
  264. int(count * 1.1),
  265. )
  266. tsdb.incr_multi(
  267. (
  268. (tsdb.models.organization_total_rejected, project.organization_id),
  269. (tsdb.models.project_total_rejected, project.id),
  270. (tsdb.models.key_total_rejected, key.id),
  271. ),
  272. now,
  273. int(count * 0.1),
  274. )
  275. frequencies = [
  276. (tsdb.models.frequent_issues_by_project, {project.id: {group.id: count}}),
  277. (tsdb.models.frequent_environments_by_group, {group.id: {environment.id: count}}),
  278. ]
  279. if release:
  280. frequencies.append(
  281. (tsdb.models.frequent_releases_by_group, {group.id: {grouprelease.id: count}})
  282. )
  283. tsdb.record_frequency_multi(frequencies, now)
  284. now = now - timedelta(hours=1)
  285. def main(num_events=1, extra_events=False, load_trends=False, slow=False):
  286. try:
  287. user = User.objects.filter(is_superuser=True)[0]
  288. except IndexError:
  289. raise Exception("No superuser exists (run `make bootstrap`)")
  290. dummy_user, _ = User.objects.get_or_create(
  291. username="dummy@example.com", defaults={"email": "dummy@example.com"}
  292. )
  293. dummy_user.set_password("dummy")
  294. dummy_user.save()
  295. mocks = (
  296. ("Massive Dynamic", ("Ludic Science",)),
  297. ("Captain Planet", ("Earth", "Fire", "Wind", "Water", "Heart")),
  298. )
  299. project_map = {}
  300. Broadcast.objects.create(
  301. title="Learn about Source Maps",
  302. message="Source maps are JSON files that contain information on how to map your transpiled source code back to their original source.",
  303. link="https://docs.sentry.io/platforms/javascript/#source-maps",
  304. )
  305. if settings.SENTRY_SINGLE_ORGANIZATION:
  306. org = Organization.get_default()
  307. print(f"Mocking org {org.name}") # NOQA
  308. else:
  309. print("Mocking org {}".format("Default")) # NOQA
  310. org, _ = Organization.objects.get_or_create(slug="default")
  311. OrganizationMember.objects.get_or_create(
  312. user=user, organization=org, role=roles.get_top_dog().id
  313. )
  314. dummy_member, _ = OrganizationMember.objects.get_or_create(
  315. user=dummy_user, organization=org, defaults={"role": roles.get_default().id}
  316. )
  317. # Allow for 0 events, if you only want transactions
  318. event1 = event2 = event3 = event4 = event5 = None
  319. for team_name, project_names in mocks:
  320. print(f"> Mocking team {team_name}") # NOQA
  321. team, _ = Team.objects.get_or_create(name=team_name, defaults={"organization": org})
  322. for project_name in project_names:
  323. print(f" > Mocking project {project_name}") # NOQA
  324. project, _ = Project.objects.get_or_create(
  325. name=project_name,
  326. defaults={
  327. "organization": org,
  328. "first_event": timezone.now(),
  329. "flags": Project.flags.has_releases,
  330. },
  331. )
  332. project_map[project_name] = project
  333. project.add_team(team)
  334. if not project.first_event:
  335. project.update(first_event=project.date_added)
  336. if not project.flags.has_releases:
  337. project.update(flags=F("flags").bitor(Project.flags.has_releases))
  338. monitor, created = Monitor.objects.get_or_create(
  339. name=next(MONITOR_NAMES),
  340. project_id=project.id,
  341. organization_id=org.id,
  342. type=MonitorType.CRON_JOB,
  343. defaults={
  344. "config": {"schedule": next(MONITOR_SCHEDULES)},
  345. "next_checkin": timezone.now() + timedelta(minutes=60),
  346. "last_checkin": timezone.now(),
  347. },
  348. )
  349. if not created:
  350. if not (monitor.config or {}).get("schedule"):
  351. monitor.config = {"schedule": next(MONITOR_SCHEDULES)}
  352. monitor.update(
  353. config=monitor.config,
  354. status=MonitorStatus.OK if randint(0, 10) < 7 else MonitorStatus.ERROR,
  355. last_checkin=timezone.now(),
  356. next_checkin=monitor.get_next_scheduled_checkin(timezone.now()),
  357. )
  358. MonitorCheckIn.objects.create(
  359. project_id=monitor.project_id,
  360. monitor=monitor,
  361. status=CheckInStatus.OK
  362. if monitor.status == MonitorStatus.OK
  363. else CheckInStatus.ERROR,
  364. )
  365. with transaction.atomic():
  366. has_release = Release.objects.filter(
  367. version=sha1(uuid4().bytes).hexdigest(),
  368. organization_id=project.organization_id,
  369. projects=project,
  370. ).exists()
  371. if not has_release:
  372. release = Release.objects.filter(
  373. version=sha1(uuid4().bytes).hexdigest(),
  374. organization_id=project.organization_id,
  375. ).first()
  376. if not release:
  377. release = Release.objects.create(
  378. version=sha1(uuid4().bytes).hexdigest(),
  379. organization_id=project.organization_id,
  380. )
  381. release.add_project(project)
  382. generate_tombstones(project, user)
  383. raw_commits = generate_commits(user)
  384. try:
  385. with transaction.atomic():
  386. repo, _ = Repository.objects.get_or_create(
  387. organization_id=org.id,
  388. provider="integrations:github",
  389. external_id="example/example",
  390. defaults={
  391. "name": "Example Repo",
  392. "url": "https://github.com/example/example",
  393. },
  394. )
  395. except IntegrityError:
  396. # for users with legacy github plugin
  397. # upgrade to the new integration
  398. repo = Repository.objects.get(
  399. organization_id=org.id,
  400. provider="github",
  401. external_id="example/example",
  402. name="Example Repo",
  403. )
  404. repo.provider = "integrations:github"
  405. repo.save()
  406. authors = set()
  407. for commit_index, raw_commit in enumerate(raw_commits):
  408. author = CommitAuthor.objects.get_or_create(
  409. organization_id=org.id,
  410. email=raw_commit["author"][1],
  411. defaults={"name": raw_commit["author"][0]},
  412. )[0]
  413. commit = Commit.objects.get_or_create(
  414. organization_id=org.id,
  415. repository_id=repo.id,
  416. key=raw_commit["key"],
  417. defaults={"author": author, "message": raw_commit["message"]},
  418. )[0]
  419. authors.add(author)
  420. for file in raw_commit["files"]:
  421. ReleaseFile.objects.get_or_create(
  422. organization_id=project.organization_id,
  423. release_id=release.id,
  424. name=file[0],
  425. file=File.objects.get_or_create(
  426. name=file[0], type="release.file", checksum="abcde" * 8, size=13043
  427. )[0],
  428. defaults={"organization_id": project.organization_id},
  429. )
  430. CommitFileChange.objects.get_or_create(
  431. organization_id=org.id, commit=commit, filename=file[0], type=file[1]
  432. )
  433. ReleaseCommit.objects.get_or_create(
  434. organization_id=org.id, release=release, commit=commit, order=commit_index
  435. )
  436. # create an unreleased commit
  437. Commit.objects.get_or_create(
  438. organization_id=org.id,
  439. repository_id=repo.id,
  440. key=sha1(uuid4().bytes).hexdigest(),
  441. defaults={
  442. "author": CommitAuthor.objects.get_or_create(
  443. organization_id=org.id, email=user.email, defaults={"name": user.name}
  444. )[0],
  445. "message": "feat: Do something to {}\n{}".format(
  446. random.choice(loremipsum.words) + ".js", make_sentence()
  447. ),
  448. },
  449. )[0]
  450. Activity.objects.create(
  451. type=Activity.RELEASE,
  452. project=project,
  453. ident=release.version,
  454. user=user,
  455. data={"version": release.version},
  456. )
  457. environment = Environment.get_or_create(project=project, name=next(ENVIRONMENTS))
  458. deploy = Deploy.objects.create(
  459. organization_id=project.organization_id,
  460. release=release,
  461. environment_id=environment.id,
  462. )
  463. release.update(
  464. commit_count=len(raw_commits),
  465. last_commit_id=commit.id,
  466. total_deploys=Deploy.objects.filter(release=release).count(),
  467. last_deploy_id=deploy.id,
  468. authors=[str(a.id) for a in authors],
  469. )
  470. ReleaseProjectEnvironment.objects.create_or_update(
  471. project=project,
  472. environment=environment,
  473. release=release,
  474. defaults={"last_deploy_id": deploy.id},
  475. )
  476. Activity.objects.create(
  477. type=Activity.DEPLOY,
  478. project=project,
  479. ident=release.version,
  480. data={
  481. "version": release.version,
  482. "deploy_id": deploy.id,
  483. "environment": environment.name,
  484. },
  485. datetime=deploy.date_finished,
  486. )
  487. # Add a bunch of additional dummy events to support pagination
  488. if extra_events:
  489. for _ in range(45):
  490. platform = next(PLATFORMS)
  491. create_sample_event(
  492. project=project,
  493. platform=platform,
  494. release=release.version,
  495. level=next(LEVELS),
  496. environment=next(ENVIRONMENTS),
  497. message="This is a mostly useless example %s exception" % platform,
  498. checksum=md5_text(platform + str(_)).hexdigest(),
  499. user=generate_user(),
  500. )
  501. for _ in range(num_events):
  502. event1 = create_sample_event(
  503. project=project,
  504. platform="python",
  505. release=release.version,
  506. environment=next(ENVIRONMENTS),
  507. user=generate_user(),
  508. )
  509. EventAttachment.objects.create(
  510. project_id=project.id,
  511. event_id=event1.event_id,
  512. name="example-logfile.txt",
  513. file_id=File.objects.get_or_create(
  514. name="example-logfile.txt",
  515. type="text/plain",
  516. checksum="abcde" * 8,
  517. size=13043,
  518. )[0].id,
  519. )
  520. event2 = create_sample_event(
  521. project=project,
  522. platform="javascript",
  523. release=release.version,
  524. environment=next(ENVIRONMENTS),
  525. sdk={"name": "raven-js", "version": "2.1.0"},
  526. user=generate_user(),
  527. )
  528. event3 = create_sample_event(project, "java")
  529. event4 = create_sample_event(
  530. project=project,
  531. platform="ruby",
  532. release=release.version,
  533. environment=next(ENVIRONMENTS),
  534. user=generate_user(),
  535. )
  536. event5 = create_sample_event(
  537. project=project,
  538. platform="cocoa",
  539. release=release.version,
  540. environment=next(ENVIRONMENTS),
  541. user=generate_user(),
  542. )
  543. create_sample_event(
  544. project=project,
  545. platform="php",
  546. release=release.version,
  547. environment=next(ENVIRONMENTS),
  548. message=LONG_MESSAGE,
  549. user=generate_user(),
  550. )
  551. create_sample_event(
  552. project=project,
  553. platform="cocoa",
  554. sample_name="react-native",
  555. release=release.version,
  556. environment=next(ENVIRONMENTS),
  557. user=generate_user(),
  558. )
  559. create_sample_event(
  560. project=project,
  561. platform="pii",
  562. release=release.version,
  563. environment=next(ENVIRONMENTS),
  564. user=generate_user(),
  565. )
  566. if event5:
  567. Commit.objects.get_or_create(
  568. organization_id=org.id,
  569. repository_id=repo.id,
  570. key=sha1(uuid4().bytes).hexdigest(),
  571. defaults={
  572. "author": CommitAuthor.objects.get_or_create(
  573. organization_id=org.id, email=user.email, defaults={"name": user.name}
  574. )[0],
  575. "message": f"Ooops!\nFixes {event5.group.qualified_short_id}",
  576. },
  577. )[0]
  578. create_sample_event(project=project, environment=next(ENVIRONMENTS), platform="csp")
  579. if event3:
  580. UserReport.objects.create(
  581. project_id=project.id,
  582. event_id=event3.event_id,
  583. group_id=event3.group.id,
  584. name="Jane Bloggs",
  585. email="jane@example.com",
  586. comments=make_sentence(),
  587. )
  588. try:
  589. # Metric alerts
  590. alert_rule = create_alert_rule(
  591. org,
  592. [project],
  593. "My Alert Rule",
  594. "level:error",
  595. "count()",
  596. 10,
  597. AlertRuleThresholdType.ABOVE,
  598. 1,
  599. )
  600. create_alert_rule_trigger(alert_rule, "critical", 10)
  601. create_incident(
  602. org,
  603. type_=IncidentType.DETECTED,
  604. title="My Incident",
  605. date_started=datetime.utcnow().replace(tzinfo=utc),
  606. alert_rule=alert_rule,
  607. projects=[project],
  608. )
  609. except AlertRuleNameAlreadyUsedError:
  610. pass
  611. print(f" > Loading time series data") # NOQA
  612. if event1:
  613. create_sample_time_series(event1, release=release)
  614. if event2:
  615. create_sample_time_series(event2, release=release)
  616. if event3:
  617. create_sample_time_series(event3)
  618. if event4:
  619. create_sample_time_series(event4, release=release)
  620. if event5:
  621. create_sample_time_series(event5, release=release)
  622. if hasattr(buffer, "process_pending"):
  623. print(" > Processing pending buffers") # NOQA
  624. buffer.process_pending()
  625. mocks_loaded.send(project=project, sender=__name__)
  626. OrganizationAccessRequest.objects.create_or_update(member=dummy_member, team=team)
  627. create_mock_transactions(project_map, load_trends, slow)
  628. Activity.objects.create(
  629. type=Activity.RELEASE,
  630. project=project,
  631. ident="4f38b65c62c4565aa94bba391ff8946922a8eed4",
  632. user=user,
  633. data={"version": "4f38b65c62c4565aa94bba391ff8946922a8eed4"},
  634. )
  635. create_system_time_series()
  636. def create_mock_transactions(project_map, load_trends=False, slow=False):
  637. backend_project = project_map["Earth"]
  638. frontend_project = project_map["Fire"]
  639. service_projects = [
  640. project_map["Wind"],
  641. project_map["Water"],
  642. project_map["Heart"],
  643. ]
  644. for project in project_map.values():
  645. if not project.flags.has_transactions:
  646. project.update(flags=F("flags").bitor(Project.flags.has_transactions))
  647. timestamp = timezone.now()
  648. print(f" > Loading a trace") # NOQA
  649. create_trace(
  650. slow,
  651. timestamp - timedelta(milliseconds=random_normal(4000, 250, 1000)),
  652. timestamp,
  653. generate_user(),
  654. uuid4().hex,
  655. None,
  656. {
  657. "project": frontend_project,
  658. "transaction": "/plants/:plantId/",
  659. "frontend": True,
  660. "errors": 1,
  661. "children": [
  662. {
  663. "project": backend_project,
  664. "transaction": "/api/plants/",
  665. "children": [
  666. {
  667. "project": service_projects[0],
  668. "transaction": "/products/all/",
  669. "children": [],
  670. },
  671. {
  672. "project": service_projects[1],
  673. "transaction": "/analytics/",
  674. "children": [],
  675. },
  676. {
  677. "project": service_projects[2],
  678. "transaction": "tasks.create_invoice",
  679. "children": [
  680. {
  681. "project": service_projects[2],
  682. "transaction": "tasks.process_invoice",
  683. "children": [
  684. {
  685. "project": service_projects[2],
  686. "transaction": "tasks.process_invoice",
  687. "children": [
  688. {
  689. "project": service_projects[2],
  690. "transaction": "tasks.process_invoice",
  691. "children": [
  692. {
  693. "project": service_projects[2],
  694. "transaction": "tasks.process_invoice",
  695. "children": [],
  696. },
  697. ],
  698. },
  699. ],
  700. },
  701. ],
  702. },
  703. ],
  704. },
  705. ],
  706. },
  707. ],
  708. },
  709. )
  710. if load_trends:
  711. print(f" > Loading trends data") # NOQA
  712. for day in range(14):
  713. for hour in range(24):
  714. timestamp = timezone.now() - timedelta(days=day, hours=hour)
  715. transaction_user = generate_user()
  716. trace_id = uuid4().hex
  717. frontend_span_id = uuid4().hex[:16]
  718. frontend_root_span_id = uuid4().hex[:16]
  719. frontend_duration = random_normal(2000 - 50 * day, 250, 1000)
  720. create_sample_event(
  721. project=frontend_project,
  722. platform="javascript-transaction",
  723. transaction="/trends/:frontend/",
  724. event_id=uuid4().hex,
  725. user=transaction_user,
  726. timestamp=timestamp,
  727. # start_timestamp decreases based on day so that there's a trend
  728. start_timestamp=timestamp - timedelta(milliseconds=frontend_duration),
  729. measurements={
  730. "fp": {"value": random_normal(1250 - 50 * day, 200, 500)},
  731. "fcp": {"value": random_normal(1250 - 50 * day, 200, 500)},
  732. "lcp": {"value": random_normal(2800 - 50 * day, 400, 2000)},
  733. "fid": {"value": random_normal(5 - 0.125 * day, 2, 1)},
  734. },
  735. # Root
  736. parent_span_id=None,
  737. span_id=frontend_root_span_id,
  738. trace=trace_id,
  739. spans=[
  740. {
  741. "same_process_as_parent": True,
  742. "op": "http",
  743. "description": "GET /api/plants/?all_plants=1",
  744. "data": {
  745. "duration": random_normal(
  746. 1 - 0.05 * day, 0.25, 0.01, frontend_duration / 1000
  747. ),
  748. "offset": 0.02,
  749. },
  750. "span_id": frontend_span_id,
  751. "trace_id": trace_id,
  752. }
  753. ],
  754. )
  755. # try to give clickhouse some breathing room
  756. if slow:
  757. time.sleep(0.05)
  758. backend_duration = random_normal(1500 + 50 * day, 250, 500)
  759. create_sample_event(
  760. project=backend_project,
  761. platform="transaction",
  762. transaction="/trends/backend/",
  763. event_id=uuid4().hex,
  764. user=transaction_user,
  765. timestamp=timestamp,
  766. start_timestamp=timestamp - timedelta(milliseconds=backend_duration),
  767. # match the trace from the javascript transaction
  768. trace=trace_id,
  769. parent_span_id=frontend_root_span_id,
  770. spans=[],
  771. )
  772. # try to give clickhouse some breathing room
  773. if slow:
  774. time.sleep(0.05)
  775. if __name__ == "__main__":
  776. settings.CELERY_ALWAYS_EAGER = True
  777. from optparse import OptionParser
  778. parser = OptionParser()
  779. parser.add_option("--events", default=1, type=int, help="number of events to generate")
  780. parser.add_option(
  781. "--extra-events",
  782. default=False,
  783. action="store_true",
  784. help="add multiple events for each error group",
  785. )
  786. parser.add_option(
  787. "--load-trends",
  788. default=False,
  789. action="store_true",
  790. help="load multiple transactions for each id to show trends",
  791. )
  792. parser.add_option(
  793. "--slow",
  794. default=False,
  795. action="store_true",
  796. help="sleep between each transaction to let clickhouse rest",
  797. )
  798. (options, args) = parser.parse_args()
  799. try:
  800. main(
  801. num_events=options.events,
  802. extra_events=options.extra_events,
  803. load_trends=options.load_trends,
  804. slow=options.slow,
  805. )
  806. except Exception:
  807. # Avoid reporting any issues recursively back into Sentry
  808. import sys
  809. import traceback
  810. traceback.print_exc()
  811. sys.exit(1)