constants.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. """
  2. These settings act as the default (base) settings for the Sentry-provided
  3. web-server
  4. """
  5. import logging
  6. import os.path
  7. import six
  8. from datetime import timedelta
  9. from collections import OrderedDict, namedtuple
  10. from django.conf import settings
  11. from django.utils.translation import gettext_lazy as _
  12. # from sentry.utils.integrationdocs import load_doc
  13. # from sentry.utils.geo import rust_geoip
  14. # import semaphore
  15. def get_all_languages():
  16. results = []
  17. for path in os.listdir(os.path.join(MODULE_ROOT, "locale")):
  18. if path.startswith("."):
  19. continue
  20. if "_" in path:
  21. pre, post = path.split("_", 1)
  22. path = "{}-{}".format(pre, post.lower())
  23. results.append(path)
  24. return results
  25. MODULE_ROOT = os.path.dirname(__import__("sentry").__file__)
  26. DATA_ROOT = os.path.join(MODULE_ROOT, "data")
  27. BAD_RELEASE_CHARS = "\n\f\t/"
  28. MAX_VERSION_LENGTH = 200
  29. MAX_COMMIT_LENGTH = 64
  30. COMMIT_RANGE_DELIMITER = ".."
  31. SORT_OPTIONS = OrderedDict(
  32. (
  33. ("priority", _("Priority")),
  34. ("date", _("Last Seen")),
  35. ("new", _("First Seen")),
  36. ("freq", _("Frequency")),
  37. )
  38. )
  39. SEARCH_SORT_OPTIONS = OrderedDict(
  40. (("score", _("Score")), ("date", _("Last Seen")), ("new", _("First Seen")))
  41. )
  42. # XXX: Deprecated: use GroupStatus instead
  43. STATUS_UNRESOLVED = 0
  44. STATUS_RESOLVED = 1
  45. STATUS_IGNORED = 2
  46. STATUS_CHOICES = {
  47. "resolved": STATUS_RESOLVED,
  48. "unresolved": STATUS_UNRESOLVED,
  49. "ignored": STATUS_IGNORED,
  50. # TODO(dcramer): remove in 9.0
  51. "muted": STATUS_IGNORED,
  52. }
  53. # Normalize counts to the 15 minute marker. This value MUST be less than 60. A
  54. # value of 0 would store counts for every minute, and is the lowest level of
  55. # accuracy provided.
  56. MINUTE_NORMALIZATION = 15
  57. MAX_TAG_KEY_LENGTH = 32
  58. MAX_TAG_VALUE_LENGTH = 200
  59. MAX_CULPRIT_LENGTH = 200
  60. MAX_EMAIL_FIELD_LENGTH = 75
  61. ENVIRONMENT_NAME_PATTERN = r"^[^\n\r\f\/]*$"
  62. ENVIRONMENT_NAME_MAX_LENGTH = 64
  63. SENTRY_APP_SLUG_MAX_LENGTH = 64
  64. # Team slugs which may not be used. Generally these are top level URL patterns
  65. # which we don't want to worry about conflicts on.
  66. RESERVED_ORGANIZATION_SLUGS = frozenset(
  67. (
  68. "admin",
  69. "manage",
  70. "login",
  71. "account",
  72. "register",
  73. "api",
  74. "accept",
  75. "organizations",
  76. "teams",
  77. "projects",
  78. "help",
  79. "docs",
  80. "logout",
  81. "404",
  82. "500",
  83. "_static",
  84. "out",
  85. "debug",
  86. "remote",
  87. "get-cli",
  88. "blog",
  89. "welcome",
  90. "features",
  91. "customers",
  92. "integrations",
  93. "signup",
  94. "pricing",
  95. "subscribe",
  96. "enterprise",
  97. "about",
  98. "jobs",
  99. "thanks",
  100. "guide",
  101. "privacy",
  102. "security",
  103. "terms",
  104. "from",
  105. "sponsorship",
  106. "for",
  107. "at",
  108. "platforms",
  109. "branding",
  110. "vs",
  111. "answers",
  112. "_admin",
  113. "support",
  114. "contact",
  115. "onboarding",
  116. "ext",
  117. "extension",
  118. "extensions",
  119. "plugins",
  120. "themonitor",
  121. "settings",
  122. "legal",
  123. "avatar",
  124. "organization-avatar",
  125. "project-avatar",
  126. "team-avatar",
  127. "careers",
  128. "_experiment",
  129. "sentry-apps",
  130. )
  131. )
  132. RESERVED_PROJECT_SLUGS = frozenset(
  133. (
  134. "api-keys",
  135. "audit-log",
  136. "auth",
  137. "members",
  138. "projects",
  139. "rate-limits",
  140. "repos",
  141. "settings",
  142. "teams",
  143. "billing",
  144. "payments",
  145. "legal",
  146. "subscription",
  147. "support",
  148. "integrations",
  149. "developer-settings",
  150. "usage",
  151. )
  152. )
  153. LOG_LEVELS = {
  154. logging.NOTSET: "sample",
  155. logging.DEBUG: "debug",
  156. logging.INFO: "info",
  157. logging.WARNING: "warning",
  158. logging.ERROR: "error",
  159. logging.FATAL: "fatal",
  160. }
  161. DEFAULT_LOG_LEVEL = "error"
  162. DEFAULT_LOGGER_NAME = ""
  163. LOG_LEVELS_MAP = {v: k for k, v in six.iteritems(LOG_LEVELS)}
  164. # Default alerting threshold values
  165. DEFAULT_ALERT_PROJECT_THRESHOLD = (500, 25) # 500%, 25 events
  166. DEFAULT_ALERT_GROUP_THRESHOLD = (1000, 25) # 1000%, 25 events
  167. # Default sort option for the group stream
  168. DEFAULT_SORT_OPTION = "date"
  169. # Setup languages for only available locales
  170. # _language_map = dict(settings.LANGUAGES)
  171. # LANGUAGES = [(k, _language_map[k]) for k in get_all_languages() if k in _language_map]
  172. # del _language_map
  173. # TODO(dcramer): We eventually want to make this user-editable
  174. TAG_LABELS = {
  175. "exc_type": "Exception Type",
  176. "sentry:user": "User",
  177. "sentry:release": "Release",
  178. "sentry:dist": "Distribution",
  179. "os": "OS",
  180. "url": "URL",
  181. "server_name": "Server",
  182. }
  183. PROTECTED_TAG_KEYS = frozenset(["environment", "release", "sentry:release"])
  184. # TODO(dcramer): once this is more flushed out we want this to be extendable
  185. SENTRY_RULES = (
  186. "sentry.rules.actions.notify_event.NotifyEventAction",
  187. "sentry.rules.actions.notify_event_service.NotifyEventServiceAction",
  188. "sentry.rules.conditions.every_event.EveryEventCondition",
  189. "sentry.rules.conditions.first_seen_event.FirstSeenEventCondition",
  190. "sentry.rules.conditions.regression_event.RegressionEventCondition",
  191. "sentry.rules.conditions.reappeared_event.ReappearedEventCondition",
  192. "sentry.rules.conditions.tagged_event.TaggedEventCondition",
  193. "sentry.rules.conditions.event_frequency.EventFrequencyCondition",
  194. "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition",
  195. "sentry.rules.conditions.event_attribute.EventAttributeCondition",
  196. "sentry.rules.conditions.level.LevelCondition",
  197. )
  198. # methods as defined by http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + PATCH
  199. HTTP_METHODS = (
  200. "GET",
  201. "POST",
  202. "PUT",
  203. "OPTIONS",
  204. "HEAD",
  205. "DELETE",
  206. "TRACE",
  207. "CONNECT",
  208. "PATCH",
  209. )
  210. # See https://github.com/getsentry/semaphore/blob/master/general/src/protocol/constants.rs
  211. # VALID_PLATFORMS = semaphore.VALID_PLATFORMS
  212. VALID_PLATFORMS = [
  213. "as3",
  214. "c",
  215. "cfml",
  216. "cocoa",
  217. "csharp",
  218. "elixir",
  219. "go",
  220. "groovy",
  221. "haskell",
  222. "java",
  223. "javascript",
  224. "native",
  225. "node",
  226. "objc",
  227. "other",
  228. "perl",
  229. "php",
  230. "python",
  231. "ruby",
  232. ]
  233. OK_PLUGIN_ENABLED = _("The {name} integration has been enabled.")
  234. OK_PLUGIN_DISABLED = _("The {name} integration has been disabled.")
  235. OK_PLUGIN_SAVED = _("Configuration for the {name} integration has been saved.")
  236. WARN_SESSION_EXPIRED = "Your session has expired." # TODO: translate this
  237. # Maximum length of a symbol
  238. MAX_SYM = 256
  239. # Known debug information file mimetypes
  240. KNOWN_DIF_FORMATS = {
  241. "text/x-breakpad": "breakpad",
  242. "application/x-mach-binary": "macho",
  243. "application/x-elf-binary": "elf",
  244. "application/x-dosexec": "pe",
  245. "application/x-ms-pdb": "pdb",
  246. "text/x-proguard+plain": "proguard",
  247. "application/x-sentry-bundle+zip": "sourcebundle",
  248. }
  249. NATIVE_UNKNOWN_STRING = "<unknown>"
  250. # Maximum number of release files that can be "skipped" (i.e., maximum paginator offset)
  251. # inside release files API endpoints.
  252. # If this number is too large, it may cause problems because of inefficient
  253. # LIMIT-OFFSET database queries.
  254. # These problems should be solved after we implement artifact bundles workflow.
  255. MAX_RELEASE_FILES_OFFSET = 20000
  256. # to go from an integration id (in _platforms.json) to the platform
  257. # data, such as documentation url or humanized name.
  258. # example: java-logback -> {"type": "framework",
  259. # "link": "https://docs.getsentry.com/hosted/clients/java/modules/logback/",
  260. # "id": "java-logback",
  261. # "name": "Logback"}
  262. INTEGRATION_ID_TO_PLATFORM_DATA = {}
  263. # def _load_platform_data():
  264. # INTEGRATION_ID_TO_PLATFORM_DATA.clear()
  265. # data = load_doc("_platforms")
  266. # if not data:
  267. # return
  268. # for platform in data["platforms"]:
  269. # integrations = platform.pop("integrations")
  270. # if integrations:
  271. # for integration in integrations:
  272. # integration_id = integration.pop("id")
  273. # if integration["type"] != "language":
  274. # integration["language"] = platform["id"]
  275. # INTEGRATION_ID_TO_PLATFORM_DATA[integration_id] = integration
  276. # _load_platform_data()
  277. # special cases where the marketing slug differs from the integration id
  278. # (in _platforms.json). missing values (for example: "java") should assume
  279. # the marketing slug is the same as the integration id:
  280. # javascript, node, python, php, ruby, go, swift, objc, java, perl, elixir
  281. MARKETING_SLUG_TO_INTEGRATION_ID = {
  282. "kotlin": "java",
  283. "scala": "java",
  284. "spring": "java",
  285. "android": "java-android",
  286. "react": "javascript-react",
  287. "angular": "javascript-angular",
  288. "angular2": "javascript-angular2",
  289. "ember": "javascript-ember",
  290. "backbone": "javascript-backbone",
  291. "vue": "javascript-vue",
  292. "express": "node-express",
  293. "koa": "node-koa",
  294. "django": "python-django",
  295. "flask": "python-flask",
  296. "sanic": "python-sanic",
  297. "tornado": "python-tornado",
  298. "celery": "python-celery",
  299. "rq": "python-rq",
  300. "bottle": "python-bottle",
  301. "pythonawslambda": "python-awslambda",
  302. "pyramid": "python-pyramid",
  303. "pylons": "python-pylons",
  304. "laravel": "php-laravel",
  305. "symfony": "php-symfony2",
  306. "rails": "ruby-rails",
  307. "sinatra": "ruby-sinatra",
  308. "dotnet": "csharp",
  309. }
  310. # to go from a marketing page slug like /for/android/ to the integration id
  311. # (in _platforms.json), for looking up documentation urls, etc.
  312. def get_integration_id_for_marketing_slug(slug):
  313. if slug in MARKETING_SLUG_TO_INTEGRATION_ID:
  314. return MARKETING_SLUG_TO_INTEGRATION_ID[slug]
  315. if slug in INTEGRATION_ID_TO_PLATFORM_DATA:
  316. return slug
  317. # special cases where the integration sent with the SDK differ from
  318. # the integration id (in _platforms.json)
  319. # {PLATFORM: {INTEGRATION_SENT: integration_id, ...}, ...}
  320. PLATFORM_INTEGRATION_TO_INTEGRATION_ID = {
  321. "java": {"java.util.logging": "java-logging"},
  322. # TODO: add more special cases...
  323. }
  324. # to go from event data to the integration id (in _platforms.json),
  325. # for example an event like:
  326. # {"platform": "java",
  327. # "sdk": {"name": "sentry-java",
  328. # "integrations": ["java.util.logging"]}} -> java-logging
  329. def get_integration_id_for_event(platform, sdk_name, integrations):
  330. if integrations:
  331. for integration in integrations:
  332. # check special cases
  333. if (
  334. platform in PLATFORM_INTEGRATION_TO_INTEGRATION_ID
  335. and integration in PLATFORM_INTEGRATION_TO_INTEGRATION_ID[platform]
  336. ):
  337. return PLATFORM_INTEGRATION_TO_INTEGRATION_ID[platform][integration]
  338. # try <platform>-<integration>, for example "java-log4j"
  339. integration_id = "%s-%s" % (platform, integration)
  340. if integration_id in INTEGRATION_ID_TO_PLATFORM_DATA:
  341. return integration_id
  342. # try sdk name, for example "sentry-java" -> "java" or "raven-java:log4j" -> "java-log4j"
  343. sdk_name = (
  344. sdk_name.lower().replace("sentry-", "").replace("raven-", "").replace(":", "-")
  345. )
  346. if sdk_name in INTEGRATION_ID_TO_PLATFORM_DATA:
  347. return sdk_name
  348. # try platform name, for example "java"
  349. if platform in INTEGRATION_ID_TO_PLATFORM_DATA:
  350. return platform
  351. class ObjectStatus(object):
  352. VISIBLE = 0
  353. HIDDEN = 1
  354. PENDING_DELETION = 2
  355. DELETION_IN_PROGRESS = 3
  356. ACTIVE = 0
  357. DISABLED = 1
  358. @classmethod
  359. def as_choices(cls):
  360. return (
  361. (cls.ACTIVE, "active"),
  362. (cls.DISABLED, "disabled"),
  363. (cls.PENDING_DELETION, "pending_deletion"),
  364. (cls.DELETION_IN_PROGRESS, "deletion_in_progress"),
  365. )
  366. class SentryAppStatus(object):
  367. UNPUBLISHED = 0
  368. PUBLISHED = 1
  369. INTERNAL = 2
  370. UNPUBLISHED_STR = "unpublished"
  371. PUBLISHED_STR = "published"
  372. INTERNAL_STR = "internal"
  373. @classmethod
  374. def as_choices(cls):
  375. return (
  376. (cls.UNPUBLISHED, cls.UNPUBLISHED_STR),
  377. (cls.PUBLISHED, cls.PUBLISHED_STR),
  378. (cls.INTERNAL, cls.INTERNAL_STR),
  379. )
  380. @classmethod
  381. def as_str(cls, status):
  382. if status == cls.UNPUBLISHED:
  383. return cls.UNPUBLISHED_STR
  384. elif status == cls.PUBLISHED:
  385. return cls.PUBLISHED_STR
  386. elif status == cls.INTERNAL:
  387. return cls.INTERNAL_STR
  388. class SentryAppInstallationStatus(object):
  389. PENDING = 0
  390. INSTALLED = 1
  391. PENDING_STR = "pending"
  392. INSTALLED_STR = "installed"
  393. @classmethod
  394. def as_choices(cls):
  395. return ((cls.PENDING, cls.PENDING_STR), (cls.INSTALLED, cls.INSTALLED_STR))
  396. @classmethod
  397. def as_str(cls, status):
  398. if status == cls.PENDING:
  399. return cls.PENDING_STR
  400. elif status == cls.INSTALLED:
  401. return cls.INSTALLED_STR
  402. StatsPeriod = namedtuple("StatsPeriod", ("segments", "interval"))
  403. LEGACY_RATE_LIMIT_OPTIONS = frozenset(
  404. ("sentry:project-rate-limit", "sentry:account-rate-limit")
  405. )
  406. # We need to limit the range of valid timestamps of an event because that
  407. # timestamp is used to control data retention.
  408. MAX_SECS_IN_FUTURE = 60
  409. MAX_SECS_IN_PAST = 2592000 # 30 days
  410. ALLOWED_FUTURE_DELTA = timedelta(seconds=MAX_SECS_IN_FUTURE)
  411. # DEFAULT_STORE_NORMALIZER_ARGS = dict(
  412. # geoip_lookup=rust_geoip,
  413. # stacktrace_frames_hard_limit=settings.SENTRY_STACKTRACE_FRAMES_HARD_LIMIT,
  414. # max_stacktrace_frames=settings.SENTRY_MAX_STACKTRACE_FRAMES,
  415. # max_secs_in_future=MAX_SECS_IN_FUTURE,
  416. # max_secs_in_past=MAX_SECS_IN_PAST,
  417. # enable_trimming=True,
  418. # )
  419. INTERNAL_INTEGRATION_TOKEN_COUNT_MAX = 20
  420. ALL_ACCESS_PROJECTS = {-1}