Guest User

Untitled

a guest
Aug 14th, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.53 KB | None | 0 0
  1. import React, { Component } from 'react'
  2. import CloudWatchLogs from 'aws-sdk/clients/cloudwatchlogs'
  3. import Fingerprint2 from 'fingerprintjs2'
  4. import StackTrace from 'stacktrace-js'
  5. import { promisify } from 'es6-promisify'
  6.  
  7. export default class Logger {
  8.  
  9. events = []
  10. originalConsole = null
  11. intervalId = null
  12.  
  13. constructor(accessKeyId, secretAccessKey, region, group, levels = ['error'], interval = 10000, mute = false) {
  14. this.valid = accessKeyId && secretAccessKey && region && group
  15. this.client = new CloudWatchLogs({ accessKeyId, secretAccessKey, region })
  16. this.client.createLogStreamAsync = promisify(this.client.createLogStream)
  17. this.client.putLogEventsAsync = promisify(this.client.putLogEvents)
  18. this.group = group
  19. this.levels = levels
  20. this.interval = interval
  21. this.mute = mute
  22. }
  23.  
  24. setCache(key, value) {
  25. global.localStorage.setItem(`ConsoleCloudWatch:${key}`, value)
  26. }
  27.  
  28. getCache(key) {
  29. return global.localStorage.getItem(`ConsoleCloudWatch:${key}`)
  30. }
  31.  
  32. deleteCache(key) {
  33. return global.localStorage.removeItem(`ConsoleCloudWatch:${key}`)
  34. }
  35.  
  36. init() {
  37. const original = {}
  38. for (const level of this.levels) {
  39. original[level] = global.console[level]
  40. global.console[level] = (message, ...args) => {
  41. this.onError(message)
  42. if (!this.mute) {
  43. original[level](message, ...args)
  44. }
  45. }
  46. }
  47. this.originalConsole = original
  48. this.intervalId = global.setInterval(this.onInterval.bind(this), this.interval)
  49. global.addEventListener('error', this.onError.bind(this))
  50. }
  51.  
  52. refresh() {
  53. this.deleteCache('key')
  54. this.deleteCache('sequenceToken')
  55. this.events.splice(0)
  56. }
  57.  
  58. async onError(e, info = {}) {
  59. if (!this.valid) {
  60. return
  61. }
  62. this.events.push({
  63. message: await this.createPushMessageFromError(e, info),
  64. timestamp: new Date().getTime(),
  65. })
  66. }
  67.  
  68. async onInterval() {
  69. if (!this.valid) {
  70. return
  71. }
  72. const pendingEvents = this.events.splice(0)
  73. if (!pendingEvents.length) {
  74. return
  75. }
  76. const key = await this.createOrRetrieveKey()
  77. if (!key) {
  78. return
  79. }
  80. const params = {
  81. logEvents: pendingEvents,
  82. logGroupName: this.group,
  83. logStreamName: key,
  84. }
  85. const sequenceToken = this.getCache('sequenceToken')
  86. if (sequenceToken) {
  87. params.sequenceToken = sequenceToken
  88. }
  89. let nextSequenceToken, match
  90. try {
  91. ({ nextSequenceToken } = await this.client.putLogEventsAsync(params))
  92. } catch (e) {
  93. if (!e || e.code !== 'InvalidSequenceTokenException' || !(match = e.message.match(/The next expected sequenceToken is: (\w+)/))) {
  94. this.originalConsole.error(e)
  95. this.refresh()
  96. return
  97. }
  98. }
  99. this.setCache('sequenceToken', nextSequenceToken || match[1])
  100. }
  101.  
  102. async createOrRetrieveKey() {
  103. let key
  104. if ((key = this.getCache('key'))) {
  105. return key
  106. }
  107. try {
  108. key = await new Promise((resolve) => new Fingerprint2().get(resolve))
  109. await this.client.createLogStreamAsync({
  110. logGroupName: this.group,
  111. logStreamName: key,
  112. })
  113. } catch (e) {
  114. if (!e || e.code !== 'ResourceAlreadyExistsException') {
  115. this.originalConsole.error(e)
  116. this.refresh()
  117. return
  118. }
  119. }
  120. this.setCache('key', key)
  121. return key
  122. }
  123.  
  124. async createPushMessageFromError(e, info = {}) {
  125. const message = e && e.message ? e.message : e
  126. const timestamp = new Date().getTime()
  127. const userAgent = global.navigator.userAgent
  128.  
  129. let stack = null
  130. if (e && e.message && e.stack) {
  131. stack = e.stack
  132. try {
  133. stack = await StackTrace.fromError(e, { offline: true })
  134. } catch (_) {
  135. }
  136. }
  137.  
  138. return JSON.stringify({
  139. message,
  140. timestamp,
  141. userAgent,
  142. stack,
  143. ...info,
  144. })
  145. }
  146.  
  147. createLoggerMiddleware() {
  148. return (store) => (next) => (action) => {
  149. try {
  150. return next(action)
  151. } catch (e) {
  152. this.onError(e, {
  153. action,
  154. state: store.getState(),
  155. category: 'redux',
  156. })
  157. }
  158. }
  159. }
  160.  
  161. createLoggerComponent() {
  162. const logger = this
  163. return class LoggerComponent extends Component {
  164.  
  165. state = {
  166. e: null,
  167. }
  168.  
  169. componentDidCatch(e, info) {
  170. this.setState({ e })
  171. logger.onError(e, {
  172. info,
  173. category: 'react',
  174. })
  175. }
  176.  
  177. render() {
  178. if (this.state.e) {
  179. return <div>Fatal Error: {this.state.e.message}</div>
  180. }
  181. return this.props.children
  182. }
  183. }
  184. }
  185. }
Add Comment
Please, Sign In to add comment