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()

Reply via email to