Repository: ray-project/ray

Issue:
Finding the latest session log files is annoying
The log files are timestamped, so if you're running many sessions and want to find the latest logs, you have to manually find the latest timestamp and then `cd` into that directory. Would be much nicer to automatically create a symlink to the latest session so you can `cat /tmp/ray/session_latest/...`.


Discussion:

Resolving patch:
diff --git a/python/ray/node.py b/python/ray/node.py
--- a/python/ray/node.py
+++ b/python/ray/node.py
@@ -19,7 +19,7 @@
 import ray.ray_constants as ray_constants
 import ray.services
 from ray.resource_spec import ResourceSpec
-from ray.utils import try_to_create_directory
+from ray.utils import try_to_create_directory, try_to_symlink_directory
 
 # Logger for this module. It should be configured at the entry point
 # into the program using Ray. Ray configures it by default automatically
@@ -27,6 +27,7 @@
 logger = logging.getLogger(__name__)
 
 PY3 = sys.version_info.major >= 3
+SESSION_LATEST = "session_latest"
 
 
 class Node(object):
@@ -171,9 +172,11 @@ def _init_temp(self, redis_client):
         else:
             self._session_dir = ray.utils.decode(
                 redis_client.get("session_dir"))
+        session_symlink = os.path.join(self._temp_dir, SESSION_LATEST)
 
         # Send a warning message if the session exists.
         try_to_create_directory(self._session_dir)
+        try_to_symlink_directory(self._session_dir, session_symlink)
         # Create a directory to be used for socket files.
         self._sockets_dir = os.path.join(self._session_dir, "sockets")
         try_to_create_directory(self._sockets_dir, warn_if_exist=False)
diff --git a/python/ray/utils.py b/python/ray/utils.py
--- a/python/ray/utils.py
+++ b/python/ray/utils.py
@@ -614,3 +614,49 @@ def try_to_create_directory(directory_path, warn_if_exist=True):
     # Change the log directory permissions so others can use it. This is
     # important when multiple people are using the same machine.
     try_make_directory_shared(directory_path)
+
+
+def try_to_symlink_directory(directory_path, symlink_path):
+    """Attempt to create a symlink to an existing directory.
+
+    If the directory doesn't exist, the symlink path exists, or the symlink
+    failed to be created, a warning will be logged and the symlink will not
+    be created. If a symlink exists in the path, it will be attempted to be
+    removed and replaced.
+
+    Args:
+        directory_path: The path of the existing directory.
+        symlink_path: The path of the symlink to create.
+    """
+    logger = logging.getLogger("ray")
+    directory_path = os.path.expanduser(directory_path)
+    symlink_path = os.path.expanduser(symlink_path)
+    if not os.path.exists(directory_path):
+        logger.warning("Attempted to create symlink to directory '{}', but "
+                       "the directory doesn't exist.".format(directory_path))
+        return
+    elif not os.path.isdir(directory_path):
+        logger.warning("Attempted to create symlink to directory '{}', but "
+                       "the '{}' isn't a directory.".format(
+                           directory_path, directory_path))
+        return
+
+    if os.path.exists(symlink_path):
+        if os.path.islink(symlink_path):
+            try:
+                os.remove(symlink_path)
+            except OSError as e:
+                logger.warning("Failed to remove existing symlink '{}': {}"
+                               .format(symlink_path, e))
+                return
+        else:
+            logger.warning("Attempted to create symlink '{}' to directory '{}'"
+                           ", but the symlink path exists and isn't a symlink."
+                           .format(symlink_path, directory_path))
+            return
+
+    try:
+        os.symlink(directory_path, symlink_path)
+    except OSError as e:
+        logger.warning("Failed to create symlink '{}' to directory '{}': {}"
+                       .format(symlink_path, directory_path, e))


Test patch: