听云 Server for Python 探针公测开启,现在加入,机械键盘抱回家!

2015-08-19 18:14:34 +08:00
 TINGYUN

24h 紧盯显示器的运维为何忽然神秘地有了空余时间?
数年如一日做好救险准备的他们为何突然能正常下班吃上生蚝撸大腰?
改变运维的命运,究竟是何人所为?运维工作变得轻松的背后又隐藏着什么?
这一切的一切,
是工作倦怠开始偷懒?还是准备跳槽另有隐情?
是捡到秘籍修得神功?还是打通二脉融会贯通?
尽请关注《走进科学》暑期特别节目《当运维偶遇听云》,让我们跟随着镜头走进这不为人知的秘密...

听云 Server


领奖姿势


听云身为 SaaS 领域唯一上市新三板的公司, 对加入公测的同学的奖励,也大方的不要不要的:

成功部署听云 Server for Python 探针,发送截图及账户名至 hashuang@tingyun.com , 前 50 名部署成功同学(以收到邮件时间为准), 雷蛇游戏键鼠套装为您双手奉上!

那么问题来了,如何加入公测呢


  1. 来,少年,请先申请一个听云账号 注册来戳我呀 , 并开通听云 Server
  2. 下载并安装 Python 探针
  3. 重启或重新启动应用服务器
  4. 成功部署 Python 探针,监控服务端应用性能,从此老板再也不用担心我因问题发现太晚而造成用户流失,开启人生 easy 模式!

活动细则


每款应用都需要听云

5801 次点击
所在节点    推广
23 条回复
glasslion
2015-08-19 18:43:25 +08:00
连 pypi 都不上传一个, 不觉得很 low 吗
zjxubinbin
2015-08-19 20:27:01 +08:00
好,我来试试先~
zjxubinbin
2015-08-19 20:35:18 +08:00
部署了半天没数据,发现竟然只支持 Django,看来我等用 Flask 的与键盘无缘了,哎~我还是洗洗睡吧~
defunct9
2015-08-19 21:37:17 +08:00
为了抢键盘,居然用手机注册账号,上服务器装了 Sys 探针发了邮件。回来发现居然要 python 探针,装上用 flask,失败。再来, django,成功。拼了!
le0rn0
2015-08-19 21:51:40 +08:00
渣渣,大晚上的给反馈个 bug 都没人理
lins05
2015-08-19 22:47:18 +08:00
license key 旁边的复制按钮的英文应该用 copy 而不是 duplicate 吧
glasslion
2015-08-20 00:09:23 +08:00
Copyright (c ) 2010-2015 New Relic, Inc. All rights reserved.

Certain inventions disclosed in this file may be claimed within
patents owned or patent applications filed by New Relic, Inc. or third
parties.

Subject to the terms of this notice, New Relic grants you a
nonexclusive, nontransferable license, without the right to
sublicense, to (a ) install and execute one copy of these files on any
number of workstations owned or controlled by you and (b ) distribute
verbatim copies of these files to third parties. As a condition to the
foregoing grant, you must provide this notice along with each copy you
distribute and you must not remove, alter, or obscure this notice. All
other use, reproduction, modification, distribution, or other
exploitation of these files is strictly prohibited, except as may be set
forth in a separate written license agreement between you and New
Relic. The terms of any such license agreement will control over this
notice. The license stated above will be automatically terminated and
revoked if you exceed its scope or violate any of the terms of this
notice.

This License does not grant permission to use the trade names,
trademarks, service marks, or product names of New Relic, except as
required for reasonable and customary use in describing the origin of
this file and reproducing the content of this notice. You may not
mark or brand this file with any trade name, trademarks, service
marks, or product names other than the original brand (if any )
provided by New Relic.

Unless otherwise expressly agreed by New Relic in a separate written
license agreement, these files are provided AS IS, WITHOUT WARRANTY OF
ANY KIND, including without any implied warranties of MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE, or NON-INFRINGEMENT. As a
condition to your use of these files, you are solely responsible for
such use. New Relic will have no liability to you for direct,
indirect, consequential, incidental, special, or punitive damages or
for lost profits or data.
glasslion
2015-08-20 00:19:58 +08:00
@Livid 这个产品的 Python 探针 代码是 完全抄袭竞争对手 New Relic 的 https://pypi.python.org/pypi/newrelic
glasslion
2015-08-20 00:22:56 +08:00
操, 公平起见, 顺便揭露下, 你们的国内的竞争对手 OneAPM 也是抄 New Relic 的

https://www.v2ex.com/t/125736
Strikeactor
2015-08-20 00:28:12 +08:00
@glasslion 隐隐感觉水深啊

前排看戏,瓜子鸡脚矿泉水
beordle
2015-08-20 00:33:41 +08:00
国内的常态了,但如果对源码改动增强很多的话,我是可以接受的。
glasslion
2015-08-20 00:45:22 +08:00
@beordle 几乎没有改动
zjxubinbin
2015-08-20 09:32:08 +08:00
@glasslion 莫不是又一个骗投资人的?
est
2015-08-20 09:46:51 +08:00
@glasslion
@Strikeactor
@beordle

copy to chinar 。
alsotang
2015-08-20 10:42:03 +08:00
newrelic 到底应该最更强的混淆。。还是应该直接开源呢。。。
TINGYUN
2015-08-21 18:11:01 +08:00
@glasslion https://pypi.python.org/pypi/tingyun 这里这里
非常感谢关注听云,欢迎提出更多宝贵的意见。
TINGYUN
2015-08-21 18:11:43 +08:00
@zjxubinbin 听云的 Python 探针是在开源的基础上、公司内部数据协议上自行研发的,对于一些其他框架、插件的开发,将会逐渐的支持。
另据研发同学的可靠消息,对 Tornado , flask 的支持,很快就会实现啦。
TINGYUN
2015-08-21 18:12:46 +08:00
@lins05 谢谢反馈,已改成 copy
lins05
2015-08-21 18:42:56 +08:00
@TINGYUN :+1:
TINGYUN
2015-08-21 19:34:42 +08:00
@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.png

newrelic-weib_transaction.py
import 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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/214359

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX