This is an automated email from the ASF dual-hosted git repository.
kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-python.git
The following commit(s) were added to refs/heads/master by this push:
new f9c5530 Support loading Python agent through sw-python CLI (#156)
f9c5530 is described below
commit f9c553025051e1d3e6e6d25477afa420d2a8994c
Author: Yihao Chen <[email protected]>
AuthorDate: Wed Aug 25 16:53:29 2021 +0800
Support loading Python agent through sw-python CLI (#156)
---
CHANGELOG.md | 1 +
README.md | 12 +++
docs/CLI.md | 87 +++++++++++++++++
docs/EnvVars.md | 1 +
setup.py | 7 +-
skywalking/bootstrap/__init__.py | 38 ++++++++
skywalking/bootstrap/cli/__init__.py | 21 ++++
skywalking/bootstrap/cli/sw_python.py | 78 +++++++++++++++
skywalking/bootstrap/cli/utility/__init__.py | 16 +++
skywalking/bootstrap/cli/utility/runner.py | 64 ++++++++++++
skywalking/bootstrap/loader/__init__.py | 27 ++++++
skywalking/bootstrap/loader/sitecustomize.py | 140 +++++++++++++++++++++++++++
skywalking/log/sw_logging.py | 5 +-
sw_python/__init__.py | 16 +++
sw_python/__main__.py | 20 ++++
sw_python/sw_python.py | 27 ++++++
16 files changed, 556 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce8e32b..4e09b10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
- Feature:
- Support collecting and reporting logs to backend (#147)
+ - Add a new `sw-python` CLI that enables agent non-intrusive integration
(#156)
- New plugins:
- Falcon Plugin (#146)
diff --git a/README.md b/README.md
index 674146e..d2b0687 100755
--- a/README.md
+++ b/README.md
@@ -49,6 +49,17 @@ in SkyWalking backend, the port of gRPC protocol is `11800`,
and the port of HTT
you should configure `collector_address` (or environment variable
`SW_AGENT_COLLECTOR_BACKEND_SERVICES`)
according to the protocol you want.
+### Non-intrusive integration (CLI)
+
+SkyWalking Python agent supports running and attaching to your awesome
applications without adding any code to your
+project. The package installation comes with a new command-line script named
`sw-python`, which you can use to run your Python-based
+applications and programs in the following manner `sw-python run python
abc.py` or `sw-python run program arg0 arg1`
+
+Please do read the [CLI Guide](docs/CLI.md) for a detailed introduction to
this new feature before using in production.
+
+You can always fall back to our traditional way of integration as introduced
below,
+which is by importing SkyWalking into your project and starting the agent.
+
### Report data via gRPC protocol (Default)
For example, if you want to use gRPC protocol to report data, configure
`collector_address`
@@ -97,6 +108,7 @@ Alternatively, you can also pass the configurations via
environment variables (s
All supported environment variables can be found [here](docs/EnvVars.md)
## Report logs with Python Agent
+
The Python agent is capable of reporting collected logs to the
backend(SkyWalking OAP), enabling Log & Trace Correlation.
Please refer to the [Log Reporter Doc](docs/LogReporter.md) for a detailed
guide.
diff --git a/docs/CLI.md b/docs/CLI.md
new file mode 100644
index 0000000..b082bc0
--- /dev/null
+++ b/docs/CLI.md
@@ -0,0 +1,87 @@
+# SkyWalking Python Agent Command-Line Interface(CLI)
+
+In earlier releases than 0.7.0, you would at least need to add the following
lines to your applications to get the agent attached and running.
+
+```python
+from skywalking import agent
+agent.start()
+```
+
+
+Now the SkyWalking Python agent implements a command-line interface that can
be utilized to attach the agent to your
+awesome applications during deployment **without changing any application
code**,
+just like the [SkyWalking Java Agent](https://github.com/apache/skywalking).
+
+## Usage
+
+Upon successful [installation of the SkyWalking Python agent via
pip](../README.md#install),
+a command-line script `sw-python` is installed in your environment(virtual env
preferred).
+
+### The `run` option
+
+Currently, the `sw-python` CLI provides a `run` option, which you can use to
execute your applications
+(either begins with the `python` command or Python-based programs like
`gunicorn` on your path)
+just like you invoke them normally, plus a prefix, the following example
demonstrates the usage.
+
+If your previous command to run your gunicorn application is:
+
+`gunicorn app.wsgi`
+
+Please change it to:
+
+`sw-python run gunicorn app.wsgi`
+
+The SkyWalking Python agent will startup along with your application shortly.
+
+Note that the command does work with multiprocessing and subprocess as long as
the `PYTHONPATH` is inherited,
+please configure the environment variables configuration accordingly based on
the general documentation.
+
+When executing commands with `sw-python run command`, your command's Python
interpreter will pick up the SkyWalking loader module.
+
+It is not safe to attach SkyWalking Agent to those commands that resides in
another Python installation
+because incompatible Python versions and mismatched SkyWalking versions can
cause problems.
+Therefore, any attempt to pass a command that uses a different Python
interpreter/ environment will not bring up
+SkyWalking Python Agent even if another SkyWalking Python agent is installed
there(no matter the version),
+and will force exit with an error message indicating the reasoning.
+
+#### Disabling child processes from starting new agents
+
+Sometimes you don't actually need the agent to monitor anything in a child
process.
+
+If you do not need the agent to get loaded for application child processes,
you can turn off the behavior by setting an environment variable.
+
+`SW_PYTHON_BOOTSTRAP_PROPAGATE` to `False`
+
+Note the auto bootstrap depends on the environment inherited by child
processes,
+thus prepending a new sitecustomize path to or removing the loader path from
the `PYTHONPATH` could prevent the agent from loading in a child process.
+
+### Configuring the agent
+
+You would normally want to provide additional configurations other than the
default ones.
+
+#### Through environment variables
+
+The currently supported method is to provide the environment variables listed
+in [EnvVars Doc](EnvVars.md) as instructed in the [README](../README.md).
+
+#### Through a sw-config.yaml
+
+Currently, only environment variable configuration is supported; an optional
`yaml` configuration is to be implemented.
+
+### Enabling CLI DEBUG mode
+
+Note the CLI is a new feature that manipulates the Python interpreter
bootstrap behaviour, there could be unsupported cases.
+
+If you encounter unexpected problems, please turn on the DEBUG mode by adding
the `-d` or `--debug` flag to your `sw-python` command, as shown below.
+
+From: `sw-python run command`
+
+To: `sw-python -d run command`
+
+Please attach the debug logs to the [SkyWalking
Issues](https://github.com/apache/skywalking/issues) section if you believe it
is a bug,
+[idea discussions](https://github.com/apache/skywalking/discussions) and [pull
requests](https://github.com/apache/skywalking-python/pulls) are always
welcomed.
+
+#### Known limitations
+
+1. The CLI may not work properly with arguments that involve double quotation
marks in some shells.
+2. The CLI and bootstrapper stdout logs could get messy in Windows shells.
diff --git a/docs/EnvVars.md b/docs/EnvVars.md
index 1fddacf..8d58aef 100644
--- a/docs/EnvVars.md
+++ b/docs/EnvVars.md
@@ -44,3 +44,4 @@ Environment Variable | Description | Default
| `SW_AGENT_LOG_REPORTER_FORMATTED` | If `True`, the log reporter will
transmit the logs as formatted. Otherwise, puts logRecord.msg and
logRecord.args into message content and tags(`argument.n`), respectively. Along
with an `exception` tag if an exception was raised. | `True` |
| `SW_AGENT_LOG_REPORTER_LAYOUT` | The log reporter formats the logRecord
message based on the layout given. | `%(asctime)s [%(threadName)s]
%(levelname)s %(name)s - %(message)s` |
| `SW_AGENT_CAUSE_EXCEPTION_DEPTH` | This config limits agent to report up to
`limit` stacktrace, please refer to [Python
traceback](https://docs.python.org/3/library/traceback.html#traceback.print_tb)
for more explanations. | `5` |
+| `SW_PYTHON_BOOTSTRAP_PROPAGATE`| This config controls the child process
agent bootstrap behavior in `sw-python` CLI, if set to `False`, a valid child
process will not boot up a SkyWalking Agent. Please refer to the [CLI
Guide](CLI.md) for details. | unset |
diff --git a/setup.py b/setup.py
index 25101df..0a7bf60 100644
--- a/setup.py
+++ b/setup.py
@@ -70,5 +70,10 @@ setup(
"Programming Language :: Python :: 3.9",
"Topic :: Software Development",
- ]
+ ],
+ entry_points={
+ "console_scripts": [
+ 'sw-python = skywalking.bootstrap.cli.sw_python:start'
+ ]
+ },
)
diff --git a/skywalking/bootstrap/__init__.py b/skywalking/bootstrap/__init__.py
new file mode 100644
index 0000000..f7bab8b
--- /dev/null
+++ b/skywalking/bootstrap/__init__.py
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+""" This sub-package is for the convenience of deployment and automation
+A CLI for running Python scripts and programs with SkyWalking Python Agent
automatically attached.
+`loader/sitecustomize.py` is invoked by the Python interpreter at startup.
+"""
+
+import logging
+
+
+def get_cli_logger():
+ """ A logger used by sw-python CLI """
+ logger = logging.getLogger('skywalking-cli')
+ ch = logging.StreamHandler()
+ formatter = logging.Formatter('%(name)s [%(levelname)s] %(message)s')
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+ logger.propagate = False
+
+ return logger
+
+
+cli_logger = get_cli_logger()
diff --git a/skywalking/bootstrap/cli/__init__.py
b/skywalking/bootstrap/cli/__init__.py
new file mode 100644
index 0000000..ae77ada
--- /dev/null
+++ b/skywalking/bootstrap/cli/__init__.py
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class SWRunnerFailure(Exception):
+ """ Exception runner fails to execute given user command """
+ pass
diff --git a/skywalking/bootstrap/cli/sw_python.py
b/skywalking/bootstrap/cli/sw_python.py
new file mode 100644
index 0000000..77fc44c
--- /dev/null
+++ b/skywalking/bootstrap/cli/sw_python.py
@@ -0,0 +1,78 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+""" This module is installed during package setup """
+import argparse
+import logging
+
+from skywalking.bootstrap import cli_logger
+from skywalking.bootstrap.cli import SWRunnerFailure
+from skywalking.bootstrap.cli.utility import runner
+
+_options = {
+ 'run': runner,
+}
+
+
+def start() -> None:
+ """ Entry point of CLI """
+ parser = argparse.ArgumentParser(description='SkyWalking Python Agent CLI',
+ epilog='Append your command, with
SkyWalking agent attached for you automatically',
+ allow_abbrev=False)
+
+ parser.add_argument('option', help='CLI options, now only supports `run`,
for help please type `sw-python -h` '
+ 'or refer to the CLI documentation',
+ choices=list(_options.keys()))
+
+ # TODO support parsing optional sw_config.yaml
+ # parser.add_argument('-config', nargs='?', type=argparse.FileType('r'),
+ # help='Optionally takes a sw_python.yaml config file')
+
+ # The command arg compress all remaining args into itself
+ parser.add_argument('command', help='Your original commands e.g. gunicorn
app.wsgi',
+ nargs=argparse.REMAINDER, metavar='command')
+
+ parser.add_argument('-d', '--debug', help='Print CLI debug logs to
stdout', action='store_true')
+
+ # To handle cases with flags and positional args in user commands
+ args = parser.parse_args() # type: argparse.Namespace
+
+ cli_logger.setLevel(logging.DEBUG if args.debug else logging.INFO)
+
+ cli_logger.debug("Args received {}".format(args))
+
+ if not args.command:
+ cli_logger.error("Command is not provided, please type `sw-python -h`
for the list of command line arguments")
+ return
+ try:
+ dispatch(args)
+ except SWRunnerFailure:
+ cli_logger.debug('Failed to run the given user application command
`{}`, '
+ 'please make sure given command is valid.'.
+ format(' '.join(args.command)))
+ return
+
+
+def dispatch(args: argparse.Namespace) -> None:
+ """ Dispatches parsed args to a worker """
+ cli_option, actual_command = args.option, args.command
+
+ cli_logger.debug("SkyWalking Python agent with CLI option '{}' and command
{}".format
+ (cli_option, actual_command))
+
+ # Dispatch actual user application command to runner
+ _options[cli_option].execute(actual_command)
diff --git a/skywalking/bootstrap/cli/utility/__init__.py
b/skywalking/bootstrap/cli/utility/__init__.py
new file mode 100644
index 0000000..b1312a0
--- /dev/null
+++ b/skywalking/bootstrap/cli/utility/__init__.py
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/skywalking/bootstrap/cli/utility/runner.py
b/skywalking/bootstrap/cli/utility/runner.py
new file mode 100644
index 0000000..451e2bd
--- /dev/null
+++ b/skywalking/bootstrap/cli/utility/runner.py
@@ -0,0 +1,64 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+""" User application command runner """
+import logging
+import os
+import platform
+import sys
+from typing import List
+
+from skywalking.bootstrap import cli_logger
+from skywalking.bootstrap.cli import SWRunnerFailure
+
+
+def execute(command: List[str]) -> None:
+ """ Set up environ and invokes the given command to replace current
process """
+
+ cli_logger.debug("SkyWalking Python agent `runner` received command
{}".format(command))
+
+ cli_logger.debug("Adding sitecustomize.py to PYTHONPATH")
+
+ from skywalking.bootstrap.loader import __file__ as loader_dir
+
+ loader_path = os.path.dirname(loader_dir)
+ new_path = ""
+
+ python_path = os.environ.get('PYTHONPATH')
+ if python_path: # If there is already a different PYTHONPATH, PREPEND to
it as we must get loaded first.
+ partitioned = python_path.split(os.path.pathsep)
+ if loader_path not in partitioned: # check if we are already there
+ new_path = os.path.pathsep.join([loader_path, python_path]) #
type: str
+
+ # When constructing sys.path PYTHONPATH is always
+ # before other paths and after interpreter invoker path, which is here or
none
+ os.environ['PYTHONPATH'] = new_path if new_path else loader_path
+ cli_logger.debug("Updated PYTHONPATH -
{}".format(os.environ['PYTHONPATH']))
+
+ # Used in sitecustomize to compare command's Python installation with CLI
+ # If not match, need to stop agent from loading, and kill the process
+ os.environ['SW_PYTHON_PREFIX'] =
os.path.realpath(os.path.normpath(sys.prefix))
+ os.environ['SW_PYTHON_VERSION'] = platform.python_version()
+
+ # Pass down the logger debug setting to the replaced process, need a new
logger there
+ os.environ['SW_PYTHON_CLI_DEBUG_ENABLED'] = 'True' if cli_logger.level ==
logging.DEBUG else 'False'
+
+ try:
+ cli_logger.debug('New process starting with file - `{}` args -
`{}`'.format(command[0], command))
+ os.execvp(command[0], command)
+ except OSError:
+ raise SWRunnerFailure
diff --git a/skywalking/bootstrap/loader/__init__.py
b/skywalking/bootstrap/loader/__init__.py
new file mode 100644
index 0000000..dc6a634
--- /dev/null
+++ b/skywalking/bootstrap/loader/__init__.py
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+
+
+def get_loader_path():
+ """ Return the path of loader
+ Don't use this in sitecustomize,
+ Somehow returns a capitalized version, behaves differently.
+ """
+
+ return os.path.dirname(__file__)
diff --git a/skywalking/bootstrap/loader/sitecustomize.py
b/skywalking/bootstrap/loader/sitecustomize.py
new file mode 100644
index 0000000..ef02e66
--- /dev/null
+++ b/skywalking/bootstrap/loader/sitecustomize.py
@@ -0,0 +1,140 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+""" This version of sitecustomize will
+1. initializes the SkyWalking Python Agent.
+2. invoke an existing sitecustomize.py.
+Not compatible with Python <= 3.3
+
+When executing commands with `sw-python run command`
+This particular sitecustomize module will be picked up by any valid replacement
+process(command) invoked by os.execvp in runner.py, thus will be seen by
potentially
+incompatible Python versions and system interpreters, which certainly will
cause problems.
+Therefore, an attempt to use command leading to a different Python interpreter
should be stopped.
+"""
+import importlib
+import logging
+import os
+import platform
+import sys
+
+
+def _get_sw_loader_logger():
+ """ Setup a new logger with passed skywalking CLI env vars,
+ don't import from skywalking, it may not be on sys.path
+ if user misuses the CLI to run programs out of scope
+ """
+ from logging import getLogger
+ logger = getLogger('skywalking-loader')
+ ch = logging.StreamHandler()
+ formatter = logging.Formatter('%(name)s [%(threadName)s] [%(levelname)s]
%(message)s')
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+ logger.propagate = False
+ if os.environ.get('SW_PYTHON_CLI_DEBUG_ENABLED') == 'True': # set from
the original CLI runner
+ logger.setLevel(level=logging.DEBUG)
+ return logger
+
+
+_sw_loader_logger = _get_sw_loader_logger()
+
+# DEBUG messages in case execution goes wrong
+_sw_loader_logger.debug('---------------sitecustomize.py---------------')
+_sw_loader_logger.debug("Successfully imported sitecustomize.py from
`{}`".format(__file__))
+_sw_loader_logger.debug('You are inside working dir - {}'.format(os.getcwd()))
+_sw_loader_logger.debug('Using Python version - {} '.format(sys.version))
+_sw_loader_logger.debug('Using executable at - {}'.format(sys.executable))
+_sw_loader_logger.debug('System Base Python executable location
{}'.format(sys.base_prefix))
+
+if sys.prefix != sys.base_prefix:
+ _sw_loader_logger.debug("[The SkyWalking agent bootstrapper is running
inside a virtual environment]")
+
+
+# It is possible that someone else also has a sitecustomize.py
+# in sys.path either set by .pth files or manually, we need to run them as well
+# The path to user sitecustomize.py is after ours in sys.path, needs to be
imported here.
+# just to be safe, remove loader thoroughly from sys.path and also sys.modules
+
+cleared_path = [p for p in sys.path if p != os.path.dirname(__file__)]
+sys.path = cleared_path # remove this version from path
+loaded = sys.modules.pop('sitecustomize', None) # pop sitecustomize from
loaded
+
+# now try to find the original sitecustomize provided in user env
+try:
+ loaded = importlib.import_module('sitecustomie')
+ _sw_loader_logger.debug("Found user sitecustomize file {},
imported".format(loaded))
+except ImportError: # ModuleNotFoundError
+ _sw_loader_logger.debug("Original sitecustomize module not found,
skipping.")
+finally: # surprise the import error by adding loaded back
+ sys.modules['sitecustomize'] = loaded
+
+
+# This sitecustomize by default doesn't remove the loader dir from PYTHONPATH,
+# Thus, subprocesses and multiprocessing also inherits this sitecustomize.py
+# This behavior can be turned off using a user provided env below
+# os.environ['SW_PYTHON_BOOTSTRAP_PROPAGATE']
+
+if os.environ.get('SW_PYTHON_BOOTSTRAP_PROPAGATE') == 'False':
+ if os.environ.get('PYTHONPATH'):
+ partitioned = os.environ['PYTHONPATH'].split(os.path.pathsep)
+ loader_path = os.path.dirname(__file__)
+ if loader_path in partitioned: # check if we are already removed by a
third-party
+ partitioned.remove(loader_path)
+ os.environ["PYTHONPATH"] = os.path.pathsep.join(partitioned)
+ _sw_loader_logger.debug("Removed loader from PYTHONPATH, spawned
process will not have agent enabled")
+
+
+# Note that users could be misusing the CLI to call a Python program that
+# their Python env doesn't have SkyWalking installed. Or even call another
+# env that has another version of SkyWalking installed, which is leads to
problems.
+# Even if `import skywalking` was successful, doesn't mean its the same one
where sw-python CLI was run.
+# Needs further checking for Python versions and prefix that passed down from
CLI in os.environ.
+
+cli_python_version = os.environ.get('SW_PYTHON_VERSION')
+cli_python_prefix = os.environ.get('SW_PYTHON_PREFIX')
+
+version_match = cli_python_version == platform.python_version()
+prefix_match = cli_python_prefix ==
os.path.realpath(os.path.normpath(sys.prefix))
+if not (version_match and prefix_match):
+
+ _sw_loader_logger.error(
+ "\nPython used by sw-python CLI - v{} at {}\n"
+ "Python used by your actual program - v{} at {}".format(
+ cli_python_version, cli_python_prefix, platform.python_version(),
+ os.path.realpath(os.path.normpath(sys.prefix))
+ )
+ )
+ _sw_loader_logger.error("The sw-python CLI was instructed to run a program
"
+ "using an different Python installation "
+ "this is not safe and loader will not proceed. "
+ "Please make sure that sw-python cli, skywalking
agent and your "
+ "application are using the same Python
installation, "
+ "Rerun with debug flag, `sw-python -d run yourapp`
for some troubleshooting information."
+ "use `which sw-python` to find out the invoked CLI
location")
+ os._exit(1) # do not go further
+
+else:
+ from skywalking import agent
+
+ # Currently supports configs read from os.environ
+
+ # noinspection PyBroadException
+ try:
+ _sw_loader_logger.debug("SkyWalking Python Agent starting, loader
finished.")
+ agent.start()
+ except Exception:
+ _sw_loader_logger.error("SkyWalking Python Agent failed to start,
please inspect your package installation")
diff --git a/skywalking/log/sw_logging.py b/skywalking/log/sw_logging.py
index 907e8df..532b873 100644
--- a/skywalking/log/sw_logging.py
+++ b/skywalking/log/sw_logging.py
@@ -17,10 +17,9 @@
import logging
+from skywalking import config, agent
from skywalking.protocol.common.Common_pb2 import KeyStringValuePair
from skywalking.protocol.logging.Logging_pb2 import LogData, LogDataBody,
TraceContext, LogTags, TextLog
-
-from skywalking import config, agent
from skywalking.trace.context import get_context
from skywalking.utils.filter import sw_traceback, sw_filter
@@ -37,7 +36,7 @@ def install():
log_reporter_level = logging.getLevelName(config.log_reporter_level) #
type: int
def _sw_handle(self, record):
- if record.name == "skywalking": # Ignore SkyWalking internal logger
+ if record.name in ["skywalking", "skywalking-cli",
"skywalking-loader"]: # Ignore SkyWalking internal loggers
return _handle(self, record)
if record.levelno < log_reporter_level:
diff --git a/sw_python/__init__.py b/sw_python/__init__.py
new file mode 100644
index 0000000..b1312a0
--- /dev/null
+++ b/sw_python/__init__.py
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/sw_python/__main__.py b/sw_python/__main__.py
new file mode 100644
index 0000000..5c978b1
--- /dev/null
+++ b/sw_python/__main__.py
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from .sw_python import start
+
+start()
diff --git a/sw_python/sw_python.py b/sw_python/sw_python.py
new file mode 100644
index 0000000..183e979
--- /dev/null
+++ b/sw_python/sw_python.py
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+""" Just an entry point script
+python -m sw_python -d run command
+or just use the setup console script
+sw-python run command after setup install
+"""
+from skywalking.bootstrap.cli import sw_python
+
+
+def start():
+ sw_python.start()