@
glasslion 同学提抄袭这词有些严肃啊,那就认真的回复下这个质疑(明明上面的回复也很认真)@Strikeactor @
beordle @
zjxubinbin @
alsotang 首先,听云绝对不会抄袭的。
听云和 New Relic 都是基于第三方的开源模块 wrapt ,加自主研发。两家都大量借用了 wrapt, requests 模块相关功能。某一语言的探针的监测原理是一样的,基于开源组件,部分源码有重叠,是自然的事情。
下面是三段听云和 New Relic 核心代码的对照。
tingyun-hooks_entrance.py//
i.v2ex.co/d0c12Dn1.png//
i.v2ex.co/TVII4A7b.pngnewrelic-weib_transaction.pyimport sys
import cgi
import base64
import time
import string
import re
import json
import logging
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
import newrelic.packages.six as six
import newrelic.api.application
import newrelic.api.transaction
import newrelic.api.object_wrapper
import newrelic.api.function_trace
_logger = logging.getLogger (__name__)
_rum_header_fragment = '<script type="text/javascript">' \
'var NREUMQ=NREUMQ||[];NREUMQ.push (["mark","firstbyte",' \
'new Date ().getTime ()]);</script>'
_rum_footer_short_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrf2","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime ()]);</script>'
_rum2_footer_short_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrfj","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime (),"%s","%s","%s","%s","%s"]);</script>'
_rum_footer_long_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);var e=document.createElement ("script");' \
'e.type="text/javascript";' \
'e.src=(("http:"===document.location.protocol )?"http:":"https:")' \
'+"//"+"%s";document.body.appendChild (e );if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrf2","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime ()]);</script>'
_rum2_footer_long_fragment = '<script type="text/javascript">' \
'if (!NREUMQ.f ){NREUMQ.f=function (){NREUMQ.push (["load",' \
'new Date ().getTime ()]);var e=document.createElement ("script");' \
'e.type="text/javascript";' \
'e.src=(("http:"===document.location.protocol )?"http:":"https:")' \
'+"//"+"%s";document.body.appendChild (e );if (NREUMQ.a )NREUMQ.a ();};' \
'NREUMQ.a=window.onload;window.onload=NREUMQ.f;};' \
'NREUMQ.push (["nrfj","%s","%s","%s","%s",%d,%d,' \
'new Date ().getTime (),"%s","%s","%s","%s","%s"]);</script>'
# Seconds since epoch for Jan 1 2000
JAN_1_2000 = time.mktime ((2000, 1, 1, 0, 0, 0, 0, 0, 0 ))
def _encode (name, key ):
s = []
# Convert name and key into bytes which are treated as integers.
key = list (six.iterbytes (six.b (key )))
for i, c in enumerate (six.iterbytes (six.b (name ))):
s.append (chr (c ^ key[i % len (key )]))
return s
if six.PY3:
def obfuscate (name, key ):
if not (name and key ):
return ''
# Always pass name and key as str to _encode ()
return str (base64.b64encode (six.b (''.join (_encode (name, key )))),
encoding='Latin-1')
else:
def obfuscate (name, key ):
if not (name and key ):
return ''
# Always pass name and key as str to _encode ()
return base64.b64encode (six.b (''.join (_encode (name, key ))))
def deobfuscate (name, key ):
if not (name and key ):
return ''
# Always pass name and key as str to _encode ()
return ''.join (_encode (six.text_type (base64.b64decode (six.b (name )),
encoding='Latin-1'), key ))
def _lookup_environ_setting (environ, name, default=False ):
flag = environ.get (name, default )
if default is None or default:
try:
flag = not flag.lower () in ['off', 'false', '0']
except AttributeError:
pass
else:
try:
flag = flag.lower () in ['on', 'true', '1']
except AttributeError:
pass
return flag
def _extract_token (cookie ):
try:
t = re.search (r"\bNRAGENT=(tk=.{16})", cookie )
token = re.search (r"^tk=([^\"<'>]+)$", t.group (1 )) if t else None
return token and token.group (1 )
except Exception:
pass
class WebTransaction (newrelic.api.transaction.Transaction ):
def __init__(self, application, environ ):
# The web transaction can be enabled/disabled by
# the value of the variable "newrelic.enabled"
# in the WSGI environ dictionary. We need to check
# this before initialising the transaction as needs
# to be passed in base class constructor. The
# default is None, which would then result in the
# base class making the decision based on whether
# application or agent as a whole are enabled.
enabled = _lookup_environ_setting (environ, 'newrelic.enabled', None )
# Initialise the common transaction base class.
newrelic.api.transaction.Transaction.__init__(self, application, enabled )
# Bail out if the transaction is running in a
# disabled state.
if not self.enabled:
return
# Will need to check the settings a number of times.
settings = self._settings
# Check for override settings from WSGI environ.
self.background_task = _lookup_environ_setting (environ, 'newrelic.set_background_task', False )
self.ignore_transaction = _lookup_environ_setting (environ,
'newrelic.ignore_transaction', False )
self.suppress_apdex = _lookup_environ_setting (environ,
'newrelic.suppress_apdex_metric', False )
self.suppress_transaction_trace = _lookup_environ_setting (environ,
'newrelic.suppress_transaction_trace', False )
self.capture_params = _lookup_environ_setting (environ,
'newrelic.capture_request_params',
settings.capture_params )
self.autorum_disabled = _lookup_environ_setting (environ,
'newrelic.disable_browser_autorum',
not settings.browser_monitoring.auto_instrument )
# Extract from the WSGI environ dictionary
# details of the URL path. This will be set as
# default path for the web transaction. This can
# be overridden by framework to be more specific
# to avoid metrics explosion problem resulting
# from too many distinct URLs for same resource
# due to use of REST style URL concepts or
# otherwise.
request_uri = environ.get ('REQUEST_URI', None )
script_name = environ.get ('SCRIPT_NAME', None )
path_info = environ.get ('PATH_INFO', None )
http_cookie = environ.get ('HTTP_COOKIE', None )
if http_cookie and ("NRAGENT" in http_cookie ):
self.rum_token = _extract_token ( http_cookie )
self.rum_trace = True if self.rum_token else False
self._request_uri = request_uri
if self._request_uri is not None:
# Need to make sure we drop off any query string
# arguments on the path if we have to fallback
# to using the original REQUEST_URI. Can't use
# attribute access on result as only support for
# Python 2.5+.
self._request_uri = urlparse.urlparse (self._request_uri )[2]
if script_name is not None or path_info is not None:
if path_info is None:
path = script_name
elif script_name is None:
path = path_info
else:
path = script_name + path_info
self.set_transaction_name (path, 'Uri', priority=1 )
if self._request_uri is None:
self._request_uri = path
else:
if self._request_uri is not None:
self.set_transaction_name (self._request_uri, 'Uri', priority=1 )
# See if the WSGI environ dictionary includes the
# special 'X-Request-Start' or 'X-Queue-Start' HTTP
# headers. These header are optional headers that can be
# set within the underlying web server or WSGI server to
# indicate when the current request was first received
# and ready to be processed. The difference between this
# time and when application starts processing the
# request is the queue time and represents how long
# spent in any explicit request queuing system, or how
# long waiting in connecting state against listener
# sockets where request needs to be proxied between any
# processes within the application server.
#
# Note that mod_wsgi sets its own distinct variables
# automatically. Initially it set mod_wsgi.queue_start,
# which equated to when Apache first accepted the
# request. This got changed to mod_wsgi.request_start
# however, and mod_wsgi.queue_start was instead used